agora inbox for [email protected]  
help / color / mirror / Atom feed
Re: Unused header file inclusion
971+ messages / 4 participants
[nested] [flat]

* Re: Unused header file inclusion
@ 2019-08-03 19:37  Andres Freund <[email protected]>
  0 siblings, 1 reply; 971+ messages in thread

From: Andres Freund @ 2019-08-03 19:37 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Hi,

On 2019-07-31 19:25:01 -0400, Tom Lane wrote:
> Andres Freund <[email protected]> writes:
> > On 2019-07-31 16:55:31 -0400, Tom Lane wrote:
> >> Yeah.  I seem to recall a proposal that nodes.h should contain
> >> 
> >> typedef struct Foo Foo;
> >> 
> >> for every node type Foo, and then the other headers would just
> >> fill in the structs, and we could get rid of a lot of ad-hoc
> >> forward struct declarations and other hackery.
> 
> > That to me just seems objectively worse. Now adding a new struct as a
> > minor implementation detail of some subsystem doesn't just require
> > recompiling the relevant files, but just about all of pg.
> 
> Er, what?  This list of typedefs would change exactly when enum NodeTag
> changes, so AFAICS your objection is bogus.

> It's true that this proposal doesn't help for structs that aren't Nodes,
> but my sense is that > 90% of our ad-hoc struct references are for Nodes.

Ah, well, I somehow assumed you were talking about all nodes. I don't
think I agree with the 90% figure. In headers I feel like most the
references are to things like Relation, Snapshot, HeapTuple, etc.


> > Right now we really have weird dependencies between largely independent
> > subsystem.
> 
> Agreed, but I think fixing that will take some actually serious design
> work.  It's not going to mechanically fall out of changing typedef rules.

No, but without finding a more workable approach than what we're doing
often doing now wrt includes and forward declares, we'll have a lot
harder time to separate subsystems out more.


> > The only reason the explicit forward declaration is needed in the common
> > case of a 'struct foo*' parameter is that C has weird visibility rules
> > about the scope of forward declarations in paramters.
> 
> Yeah, but there's not much we can do about that, nor is getting rid
> of typedefs in favor of "struct" going to make it even a little bit
> better.

It imo pretty fundamentally does. You cannot redefine typedefs, but you
can forward declare structs.


E.g. in the attached series of patches, I'm removing a good portion of
unnecessary dependencies to fmgr.h. But to actually make a difference
that requires referencing two structs without including the header - and
I don't think restructing fmgr.h into two headers is a particularly
attractive alternative (would make it a lot more work and a lot more
invasive).

Think the first three are pretty clearly a good idea, I'm a bit less
sanguine about the fourth:
Headers like utils/timestamp.h are often included just because we need a
TimestampTz type somewhere, or call GetCurrentTimestamp(). Approximately
none of these need the PG_GETARG_* macros, which are the only reason for
including fmgr.h in these headers.  As they're macros that's not
actually needed, although I think normally good style. But I' think here
avoiding exposing fmgr.h to more headers is a bigger win.

Greetings,

Andres Freund


^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-16 23:07  Andres Freund <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 1 reply; 971+ messages in thread

From: Andres Freund @ 2019-08-16 23:07 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Hi,

On 2019-08-03 12:37:33 -0700, Andres Freund wrote:
> Think the first three are pretty clearly a good idea, I'm a bit less
> sanguine about the fourth:
> Headers like utils/timestamp.h are often included just because we need a
> TimestampTz type somewhere, or call GetCurrentTimestamp(). Approximately
> none of these need the PG_GETARG_* macros, which are the only reason for
> including fmgr.h in these headers.  As they're macros that's not
> actually needed, although I think normally good style. But I' think here
> avoiding exposing fmgr.h to more headers is a bigger win.

I still think the fourth is probably worthwhile, but I don't feel
confident enough to do it without somebody else +0.5'ing it...

I've pushed the other ones.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-18 18:37  Tom Lane <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 2 replies; 971+ messages in thread

From: Tom Lane @ 2019-08-18 18:37 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Andres Freund <[email protected]> writes:
> I've pushed the other ones.

Checking whether header files compile standalone shows you were overly
aggressive about removing fmgr.h includes:

In file included from /tmp/headerscheck.Ss8bVx/test.c:3:
./src/include/utils/selfuncs.h:143: error: expected declaration specifiers or '...' before 'FmgrInfo'
./src/include/utils/selfuncs.h:146: error: expected declaration specifiers or '...' before 'FmgrInfo'
./src/include/utils/selfuncs.h:152: error: expected declaration specifiers or '...' before 'FmgrInfo'

That's with a script I use that's like cpluspluscheck except it tests
with plain gcc not g++.  I attached it for the archives' sake.

Oddly, cpluspluscheck does not complain about those cases, but it
does complain about

In file included from /tmp/cpluspluscheck.FgX2SW/test.cpp:4:
./src/bin/scripts/scripts_parallel.h:18: error: ISO C++ forbids declaration of 'PGconn' with no type
./src/bin/scripts/scripts_parallel.h:18: error: expected ';' before '*' token
./src/bin/scripts/scripts_parallel.h:29: error: 'PGconn' has not been declared

(My headerscheck script is missing that header; I need to update it to
match the latest version of cpluspluscheck.)

			regards, tom lane



Attachments:

  [text/x-shellscript] headerscheck (1.7K, ../../[email protected]/2-headerscheck)
  download | inline:
#!/bin/sh

# Check all exported PostgreSQL include files for standalone build.

# Run this from the top-level source directory after performing a build.
# No output if everything is OK, else compiler errors.

me=`basename $0`

tmp=`mktemp -d /tmp/$me.XXXXXX`

trap 'rm -rf $tmp' 0 1 2 3 15

# This should match what configure chooses, though we only care about -Wxxx
CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -D_GNU_SOURCE -I . -I src/include -I src/interfaces/libpq $CFLAGS"

# Omit src/include/port/, because it's platform specific, and c.h includes
# the relevant file anyway.
# rusagestub.h is also platform-specific, and will be included by
# utils/pg_rusage.h if necessary.
# access/rmgrlist.h is not meant to be included standalone.
# regex/regerrs.h is not meant to be included standalone.
# parser/gram.h will be included by parser/gramparse.h.
# parser/kwlist.h is not meant to be included standalone.
# pg_trace.h and utils/probes.h can include sys/sdt.h from SystemTap,
# which itself contains C++ code and so won't compile with a C++
# compiler under extern "C" linkage.

for f in `find src/include src/interfaces/libpq/libpq-fe.h src/interfaces/libpq/libpq-events.h -name '*.h' -print | \
    grep -v -e ^src/include/port/ \
	-e ^src/include/rusagestub.h -e ^src/include/regex/regerrs.h \
	-e ^src/include/access/rmgrlist.h \
	-e ^src/include/parser/gram.h -e ^src/include/parser/kwlist.h \
	-e ^src/include/pg_trace.h -e ^src/include/utils/probes.h`
do
	{
	    test $f != "src/include/postgres_fe.h" && echo '#include "postgres.h"'
	    echo "#include \"$f\""
	} >$tmp/test.c

	${CC:-gcc} $CFLAGS -c $tmp/test.c -o $tmp/test.o
done

^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 00:50  Tom Lane <[email protected]>
  parent: Tom Lane <[email protected]>
  1 sibling, 1 reply; 971+ messages in thread

From: Tom Lane @ 2019-08-19 00:50 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

I wrote:
> (My headerscheck script is missing that header; I need to update it to
> match the latest version of cpluspluscheck.)

I did that, and ended up with the attached.  I'm rather tempted to stick
this into src/tools/ alongside cpluspluscheck, because it seems to find
rather different trouble spots than cpluspluscheck does.  Thoughts?

As of HEAD (927f34ce8), I get clean passes from both this and
cpluspluscheck, except for the FmgrInfo references in selfuncs.h.
I tried it on RHEL/Fedora, FreeBSD 12, and current macOS.

			regards, tom lane



Attachments:

  [text/x-shellscript] headerscheck (5.8K, ../../[email protected]/2-headerscheck)
  download | inline:
#!/bin/sh

# Check (almost) all PostgreSQL include files for standalone build.
#
# Argument 1 is the top-level source directory, argument 2 the
# top-level build directory (they might be the same). If not set, they
# default to the current directory.
#
# Needs to be run after configuring and creating all generated headers.
# It's advisable to configure --with-perl --with-python, or you're
# likely to get errors from associated headers.
#
# No output if everything is OK, else compiler errors.

if [ -z "$1" ]; then
    srcdir="."
else
    srcdir="$1"
fi

if [ -z "$2" ]; then
    builddir="."
else
    builddir="$2"
fi

me=`basename $0`

# Pull some info from configure's results.
MGLOB="$builddir/src/Makefile.global"
CFLAGS=`sed -n 's/^CFLAGS[ 	]*=[ 	]*//p' "$MGLOB"`
CPPFLAGS=`sed -n 's/^CPPFLAGS[ 	]*=[ 	]*//p' "$MGLOB"`
PG_SYSROOT=`sed -n 's/^PG_SYSROOT[ 	]*=[ 	]*//p' "$MGLOB"`
CC=`sed -n 's/^CC[ 	]*=[ 	]*//p' "$MGLOB"`
perl_includespec=`sed -n 's/^perl_includespec[ 	]*=[ 	]*//p' "$MGLOB"`
python_includespec=`sed -n 's/^python_includespec[ 	]*=[ 	]*//p' "$MGLOB"`

# needed on Darwin
CPPFLAGS=`echo "$CPPFLAGS" | sed "s|\\$(PG_SYSROOT)|$PG_SYSROOT|g"`

# (EXTRAFLAGS is not set here, but user can pass it in if need be.)

# Create temp directory.
tmp=`mktemp -d /tmp/$me.XXXXXX`

trap 'rm -rf $tmp' 0 1 2 3 15

# Scan all of src/ and contrib/ for header files.
for f in `cd "$srcdir" && find src contrib -name '*.h' -print`
do
	# Ignore files that are unportable or intentionally not standalone.

	# These files are platform-specific, and c.h will include the
	# one that's relevant for our current platform anyway.
	test "$f" = src/include/port/aix.h && continue
	test "$f" = src/include/port/cygwin.h && continue
	test "$f" = src/include/port/darwin.h && continue
	test "$f" = src/include/port/freebsd.h && continue
	test "$f" = src/include/port/hpux.h && continue
	test "$f" = src/include/port/linux.h && continue
	test "$f" = src/include/port/netbsd.h && continue
	test "$f" = src/include/port/openbsd.h && continue
	test "$f" = src/include/port/solaris.h && continue
	test "$f" = src/include/port/win32.h && continue

	# Additional Windows-specific headers.
	test "$f" = src/include/port/win32_port.h && continue
	test "$f" = src/include/port/win32/sys/socket.h && continue
	test "$f" = src/include/port/win32_msvc/dirent.h && continue
	test "$f" = src/port/pthread-win32.h && continue

	# Likewise, these files are platform-specific, and the one
	# relevant to our platform will be included by atomics.h.
	test "$f" = src/include/port/atomics/arch-arm.h && continue
	test "$f" = src/include/port/atomics/arch-hppa.h && continue
	test "$f" = src/include/port/atomics/arch-ia64.h && continue
	test "$f" = src/include/port/atomics/arch-ppc.h && continue
	test "$f" = src/include/port/atomics/arch-x86.h && continue
	test "$f" = src/include/port/atomics/fallback.h && continue
	test "$f" = src/include/port/atomics/generic.h && continue
	test "$f" = src/include/port/atomics/generic-acc.h && continue
	test "$f" = src/include/port/atomics/generic-gcc.h && continue
	test "$f" = src/include/port/atomics/generic-msvc.h && continue
	test "$f" = src/include/port/atomics/generic-sunpro.h && continue
	test "$f" = src/include/port/atomics/generic-xlc.h && continue

	# rusagestub.h is also platform-specific, and will be included
	# by utils/pg_rusage.h if necessary.
	test "$f" = src/include/rusagestub.h && continue

	# sepgsql.h depends on headers that aren't there on most platforms.
	test "$f" = contrib/sepgsql/sepgsql.h && continue

	# These files are not meant to be included standalone, because
	# they contain lists that might have multiple use-cases.
	test "$f" = src/include/access/rmgrlist.h && continue
	test "$f" = src/include/parser/kwlist.h && continue
	test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue
	test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue
	test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue
	test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue
	test "$f" = src/include/regex/regerrs.h && continue
	test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue
	test "$f" = src/pl/plpython/spiexceptions.h && continue
	test "$f" = src/pl/tcl/pltclerrcodes.h && continue

	# We can't make these Bison output files compilable standalone
	# without using "%code require", which old Bison versions lack.
	# parser/gram.h will be included by parser/gramparse.h anyway.
	test "$f" = src/include/parser/gram.h && continue
	test "$f" = src/backend/parser/gram.h && continue
	test "$f" = src/pl/plpgsql/src/pl_gram.h && continue
	test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue

	# This produces a "no previous prototype" warning.
	test "$f" = src/include/storage/checksum_impl.h && continue

	# ppport.h is not under our control, so we can't make it standalone.
	test "$f" = src/pl/plperl/ppport.h && continue

	# regression.h is not actually C, but ECPG code.
	test "$f" = src/interfaces/ecpg/test/regression.h && continue
	# printf_hack.h produces "unused function" warnings.
	test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue

	# OK, create .c file to include this .h file.
	{
	    test "$f" != src/include/postgres_fe.h && echo '#include "postgres.h"'
	    echo "#include \"$f\""
	} >$tmp/test.c

	# Some subdirectories need extra -I switches.
	case "$f" in
	    src/pl/plperl/*)
		EXTRAINCLUDES="$perl_includespec" ;;
	    src/pl/plpython/*)
		EXTRAINCLUDES="$python_includespec" ;;
	    src/interfaces/ecpg/*)
		EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;;
	    *)
		EXTRAINCLUDES="" ;;
	esac

	# Run the test.
	${CC:-gcc} $CPPFLAGS $CFLAGS -I $builddir -I $srcdir \
		-I $builddir/src/include -I $srcdir/src/include \
		-I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \
		$EXTRAINCLUDES $EXTRAFLAGS -c $tmp/test.c -o $tmp/test.o

done

^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 14:22  Alvaro Herrera <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 1 reply; 971+ messages in thread

From: Alvaro Herrera @ 2019-08-19 14:22 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Andres Freund <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

On 2019-Aug-18, Tom Lane wrote:

> I wrote:
> > (My headerscheck script is missing that header; I need to update it to
> > match the latest version of cpluspluscheck.)
> 
> I did that, and ended up with the attached.  I'm rather tempted to stick
> this into src/tools/ alongside cpluspluscheck, because it seems to find
> rather different trouble spots than cpluspluscheck does.  Thoughts?

Yeah, let's include this.  I've written its equivalent a couple of times
already.  (My strategy is just to compile the .h file directly though,
which creates a .gch file, rather than writing a temp .c file.)


-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 18:23  Tom Lane <[email protected]>
  parent: Alvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Tom Lane @ 2019-08-19 18:23 UTC (permalink / raw)
  To: Alvaro Herrera <[email protected]>; +Cc: Andres Freund <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Alvaro Herrera <[email protected]> writes:
> On 2019-Aug-18, Tom Lane wrote:
>> I did that, and ended up with the attached.  I'm rather tempted to stick
>> this into src/tools/ alongside cpluspluscheck, because it seems to find
>> rather different trouble spots than cpluspluscheck does.  Thoughts?

> Yeah, let's include this.

Done.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 20:07  Andres Freund <[email protected]>
  parent: Tom Lane <[email protected]>
  1 sibling, 1 reply; 971+ messages in thread

From: Andres Freund @ 2019-08-19 20:07 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Hi,

On 2019-08-18 14:37:34 -0400, Tom Lane wrote:
> Andres Freund <[email protected]> writes:
> > I've pushed the other ones.
> 
> Checking whether header files compile standalone shows you were overly
> aggressive about removing fmgr.h includes:
> 
> In file included from /tmp/headerscheck.Ss8bVx/test.c:3:
> ./src/include/utils/selfuncs.h:143: error: expected declaration specifiers or '...' before 'FmgrInfo'
> ./src/include/utils/selfuncs.h:146: error: expected declaration specifiers or '...' before 'FmgrInfo'
> ./src/include/utils/selfuncs.h:152: error: expected declaration specifiers or '...' before 'FmgrInfo'

Darn.  Pushed the obvious fix of adding a direct fmgr.h include, rather
than the preivous indirect include.


> That's with a script I use that's like cpluspluscheck except it tests
> with plain gcc not g++.  I attached it for the archives' sake.
> 
> Oddly, cpluspluscheck does not complain about those cases, but it
> does complain about

Hm. I don't understand why it's not complaining. Wonder if it's a
question of the flags or such.


> In file included from /tmp/cpluspluscheck.FgX2SW/test.cpp:4:
> ./src/bin/scripts/scripts_parallel.h:18: error: ISO C++ forbids declaration of 'PGconn' with no type
> ./src/bin/scripts/scripts_parallel.h:18: error: expected ';' before '*' token
> ./src/bin/scripts/scripts_parallel.h:29: error: 'PGconn' has not been declared

I noticed that "manually" earlier, when looking at the openbsd issue.


> (My headerscheck script is missing that header; I need to update it to
> match the latest version of cpluspluscheck.)

I wonder if we should just add a #ifndef HEADERCHECK or such to the
headers that we don't want to process as standalone headers (or #ifndef
NOT_STANDALONE or whatever). That way multiple tools can rely on these markers,
rather than copying knowledge about that kind of information into
multiple places.

I wish we could move the whole logic of those scripts into makefiles, so
we could employ parallelism.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 20:46  Andres Freund <[email protected]>
  parent: Andres Freund <[email protected]>
  0 siblings, 2 replies; 971+ messages in thread

From: Andres Freund @ 2019-08-19 20:46 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Hi,

On 2019-08-19 13:07:50 -0700, Andres Freund wrote:
> On 2019-08-18 14:37:34 -0400, Tom Lane wrote:
> > That's with a script I use that's like cpluspluscheck except it tests
> > with plain gcc not g++.  I attached it for the archives' sake.
> > 
> > Oddly, cpluspluscheck does not complain about those cases, but it
> > does complain about
> 
> Hm. I don't understand why it's not complaining. Wonder if it's a
> question of the flags or such.

I'ts caused by -fsyntax-only

# These switches are g++ specific, you may override if necessary.
CXXFLAGS=${CXXFLAGS:- -fsyntax-only -Wall}

which also explains why your headercheck doesn't have that problem, it
doesn't use -fsyntax-only.


> > (My headerscheck script is missing that header; I need to update it to
> > match the latest version of cpluspluscheck.)
> 
> I wonder if we should just add a #ifndef HEADERCHECK or such to the
> headers that we don't want to process as standalone headers (or #ifndef
> NOT_STANDALONE or whatever). That way multiple tools can rely on these markers,
> rather than copying knowledge about that kind of information into
> multiple places.
> 
> I wish we could move the whole logic of those scripts into makefiles, so
> we could employ parallelism.

Hm. Perhaps the way to do that would be to use gcc's -include to include
postgres.h, and use -Wc++-compat to detect c++ issues, rather than using
g++. Without tempfiles it ought to be a lot easier to just do all of the
relevant work in make, without a separate shell script. The
python/perl/ecpg logic would b e abit annoying, but probably not too
bad? Think we could just always add all of them?

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 21:18  Alvaro Herrera <[email protected]>
  parent: Andres Freund <[email protected]>
  1 sibling, 0 replies; 971+ messages in thread

From: Alvaro Herrera @ 2019-08-19 21:18 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Tom Lane <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

On 2019-Aug-19, Andres Freund wrote:

> > I wish we could move the whole logic of those scripts into makefiles, so
> > we could employ parallelism.
> 
> Hm. Perhaps the way to do that would be to use gcc's -include to include
> postgres.h, and use -Wc++-compat to detect c++ issues, rather than using
> g++. Without tempfiles it ought to be a lot easier to just do all of the
> relevant work in make, without a separate shell script.

I used to have this:
https://postgr.es/m/[email protected]
Not sure how much this helps, since it's a shell line in make, so not
very paralellizable.  And you still have to build the exclusions
somehow.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* Re: Unused header file inclusion
@ 2019-08-19 22:09  Tom Lane <[email protected]>
  parent: Andres Freund <[email protected]>
  1 sibling, 0 replies; 971+ messages in thread

From: Tom Lane @ 2019-08-19 22:09 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Alvaro Herrera <[email protected]>; vignesh C <[email protected]>; pgsql-hackers

Andres Freund <[email protected]> writes:
> On 2019-08-19 13:07:50 -0700, Andres Freund wrote:
>> On 2019-08-18 14:37:34 -0400, Tom Lane wrote:
>>> Oddly, cpluspluscheck does not complain about those cases, but it
>>> does complain about

>> Hm. I don't understand why it's not complaining. Wonder if it's a
>> question of the flags or such.

> I'ts caused by -fsyntax-only

Ah-hah.  Should we change that to something else?  That's probably
a hangover from thinking that all we had to do was check for C++
keywords.

>> I wish we could move the whole logic of those scripts into makefiles, so
>> we could employ parallelism.

I can't really get excited about expending a whole bunch of additional
work on these scripts.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 971+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 971+ messages in thread


end of thread, other threads:[~2026-04-02 18:59 UTC | newest]

Thread overview: 971+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-08-03 19:37 Re: Unused header file inclusion Andres Freund <[email protected]>
2019-08-16 23:07 ` Andres Freund <[email protected]>
2019-08-18 18:37   ` Tom Lane <[email protected]>
2019-08-19 00:50     ` Tom Lane <[email protected]>
2019-08-19 14:22       ` Alvaro Herrera <[email protected]>
2019-08-19 18:23         ` Tom Lane <[email protected]>
2019-08-19 20:07     ` Andres Freund <[email protected]>
2019-08-19 20:46       ` Andres Freund <[email protected]>
2019-08-19 21:18         ` Alvaro Herrera <[email protected]>
2019-08-19 22:09         ` Tom Lane <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox