0s autopkgtest [17:37:10]: starting date: 2024-03-11 0s autopkgtest [17:37:10]: git checkout: d9c0295 adt_testbed.py: supress warnings from apt using a shell pipeline 0s autopkgtest [17:37:10]: host juju-7f2275-prod-proposed-migration-environment-2; command line: /home/ubuntu/autopkgtest/runner/autopkgtest --output-dir /tmp/autopkgtest-work.kgb8jmy8/out --timeout-copy=6000 --setup-commands /home/ubuntu/autopkgtest-cloud/worker-config-production/setup-canonical.sh --setup-commands /home/ubuntu/autopkgtest/setup-commands/setup-testbed --apt-pocket=proposed=src:postgresql-16,src:db5.3,src:gdbm,src:llvm-toolchain-17,src:mmdebstrap,src:openssl,src:perl,src:python3.12,src:readline --apt-upgrade slony1-2 --timeout-short=300 --timeout-copy=20000 --timeout-build=20000 '--env=ADT_TEST_TRIGGERS=postgresql-16/16.2-1ubuntu2 db5.3/5.3.28+dfsg2-5 gdbm/1.23-5.1 llvm-toolchain-17/1:17.0.6-8ubuntu1.1 mmdebstrap/1.4.3-6 openssl/3.0.13-0ubuntu1 perl/5.38.2-3.2 python3.12/3.12.2-4build2 readline/8.2-3.1' -- ssh -s /home/ubuntu/autopkgtest/ssh-setup/nova -- --flavor autopkgtest --security-groups autopkgtest-juju-7f2275-prod-proposed-migration-environment-2@bos03-arm64-12.secgroup --name adt-noble-arm64-slony1-2-20240311-173710-juju-7f2275-prod-proposed-migration-environment-2 --image adt/ubuntu-noble-arm64-server --keyname testbed-juju-7f2275-prod-proposed-migration-environment-2 --net-id=net_prod-proposed-migration -e TERM=linux -e ''"'"'http_proxy=http://squid.internal:3128'"'"'' -e ''"'"'https_proxy=http://squid.internal:3128'"'"'' -e ''"'"'no_proxy=127.0.0.1,127.0.1.1,login.ubuntu.com,localhost,localdomain,novalocal,internal,archive.ubuntu.com,ports.ubuntu.com,security.ubuntu.com,ddebs.ubuntu.com,changelogs.ubuntu.com,launchpadlibrarian.net,launchpadcontent.net,launchpad.net,10.24.0.0/24,keystone.ps5.canonical.com,objectstorage.prodstack5.canonical.com'"'"'' --mirror=http://ftpmaster.internal/ubuntu/ 69s autopkgtest [17:38:19]: @@@@@@@@@@@@@@@@@@@@ test bed setup 69s Get:1 http://ftpmaster.internal/ubuntu noble-proposed InRelease [117 kB] 70s Get:2 http://ftpmaster.internal/ubuntu noble-proposed/main Sources [452 kB] 70s Get:3 http://ftpmaster.internal/ubuntu noble-proposed/multiverse Sources [38.0 kB] 70s Get:4 http://ftpmaster.internal/ubuntu noble-proposed/restricted Sources [3976 B] 70s Get:5 http://ftpmaster.internal/ubuntu noble-proposed/universe Sources [2673 kB] 70s Get:6 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 Packages [593 kB] 70s Get:7 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 c-n-f Metadata [3144 B] 70s Get:8 http://ftpmaster.internal/ubuntu noble-proposed/restricted arm64 Packages [20.3 kB] 70s Get:9 http://ftpmaster.internal/ubuntu noble-proposed/restricted arm64 c-n-f Metadata [116 B] 70s Get:10 http://ftpmaster.internal/ubuntu noble-proposed/universe arm64 Packages [2980 kB] 70s Get:11 http://ftpmaster.internal/ubuntu noble-proposed/universe arm64 c-n-f Metadata [8528 B] 70s Get:12 http://ftpmaster.internal/ubuntu noble-proposed/multiverse arm64 Packages [39.6 kB] 70s Get:13 http://ftpmaster.internal/ubuntu noble-proposed/multiverse arm64 c-n-f Metadata [116 B] 72s Fetched 6929 kB in 1s (4941 kB/s) 72s Reading package lists... 75s Reading package lists... 75s Building dependency tree... 75s Reading state information... 75s Calculating upgrade... 76s The following packages will be REMOVED: 76s libdb5.3 libgdbm-compat4 libgdbm6 libperl5.38 libreadline8 libssl3 76s The following NEW packages will be installed: 76s libdb5.3t64 libgdbm-compat4t64 libgdbm6t64 libperl5.38t64 libreadline8t64 76s libssl3t64 76s The following packages will be upgraded: 76s klibc-utils libklibc libpython3.12-minimal libpython3.12-stdlib openssl perl 76s perl-base perl-modules-5.38 python3.12 python3.12-minimal readline-common 76s 11 upgraded, 6 newly installed, 6 to remove and 0 not upgraded. 76s Need to get 19.4 MB of archives. 76s After this operation, 275 kB of additional disk space will be used. 76s Get:1 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 openssl arm64 3.0.13-0ubuntu1 [983 kB] 76s Get:2 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libssl3t64 arm64 3.0.13-0ubuntu1 [1770 kB] 76s Get:3 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 python3.12 arm64 3.12.2-4build2 [645 kB] 76s Get:4 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 python3.12-minimal arm64 3.12.2-4build2 [2189 kB] 76s Get:5 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libpython3.12-minimal arm64 3.12.2-4build2 [829 kB] 77s Get:6 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libreadline8t64 arm64 8.2-3.1 [153 kB] 77s Get:7 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libpython3.12-stdlib arm64 3.12.2-4build2 [2002 kB] 77s Get:8 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libdb5.3t64 arm64 5.3.28+dfsg2-5 [719 kB] 77s Get:9 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libgdbm6t64 arm64 1.23-5.1 [34.3 kB] 77s Get:10 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libgdbm-compat4t64 arm64 1.23-5.1 [6576 B] 77s Get:11 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libperl5.38t64 arm64 5.38.2-3.2 [4771 kB] 77s Get:12 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 perl arm64 5.38.2-3.2 [231 kB] 77s Get:13 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 perl-base arm64 5.38.2-3.2 [1777 kB] 77s Get:14 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 perl-modules-5.38 all 5.38.2-3.2 [3110 kB] 77s Get:15 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 readline-common all 8.2-3.1 [56.4 kB] 77s Get:16 http://ftpmaster.internal/ubuntu noble/main arm64 klibc-utils arm64 2.0.13-4 [114 kB] 77s Get:17 http://ftpmaster.internal/ubuntu noble/main arm64 libklibc arm64 2.0.13-4 [51.4 kB] 78s Fetched 19.4 MB in 1s (14.3 MB/s) 78s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74748 files and directories currently installed.) 78s Preparing to unpack .../openssl_3.0.13-0ubuntu1_arm64.deb ... 78s Unpacking openssl (3.0.13-0ubuntu1) over (3.0.10-1ubuntu4) ... 78s dpkg: libssl3:arm64: dependency problems, but removing anyway as you requested: 78s wget depends on libssl3 (>= 3.0.0). 78s u-boot-tools depends on libssl3 (>= 3.0.0). 78s tnftp depends on libssl3 (>= 3.0.0). 78s tcpdump depends on libssl3 (>= 3.0.0). 78s systemd-resolved depends on libssl3 (>= 3.0.0). 78s systemd depends on libssl3 (>= 3.0.0). 78s sudo depends on libssl3 (>= 3.0.0). 78s sbsigntool depends on libssl3 (>= 3.0.0). 78s rsync depends on libssl3 (>= 3.0.0). 78s python3-cryptography depends on libssl3 (>= 3.0.0). 78s openssh-server depends on libssl3 (>= 3.0.10). 78s openssh-client depends on libssl3 (>= 3.0.10). 78s mtd-utils depends on libssl3 (>= 3.0.0). 78s mokutil depends on libssl3 (>= 3.0.0). 78s linux-headers-6.8.0-11-generic depends on libssl3 (>= 3.0.0). 78s libsystemd-shared:arm64 depends on libssl3 (>= 3.0.0). 78s libssh-4:arm64 depends on libssl3 (>= 3.0.0). 78s libsasl2-modules:arm64 depends on libssl3 (>= 3.0.0). 78s libsasl2-2:arm64 depends on libssl3 (>= 3.0.0). 78s libpython3.12-minimal:arm64 depends on libssl3 (>= 3.0.0). 78s libnvme1 depends on libssl3 (>= 3.0.0). 78s libkrb5-3:arm64 depends on libssl3 (>= 3.0.0). 78s libkmod2:arm64 depends on libssl3 (>= 3.0.0). 78s libfido2-1:arm64 depends on libssl3 (>= 3.0.0). 78s libcurl4:arm64 depends on libssl3 (>= 3.0.0). 78s libcryptsetup12:arm64 depends on libssl3 (>= 3.0.0). 78s kmod depends on libssl3 (>= 3.0.0). 78s dhcpcd-base depends on libssl3 (>= 3.0.0). 78s bind9-libs:arm64 depends on libssl3 (>= 3.0.0). 78s 78s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74748 files and directories currently installed.) 78s Removing libssl3:arm64 (3.0.10-1ubuntu4) ... 78s Selecting previously unselected package libssl3t64:arm64. 78s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74737 files and directories currently installed.) 78s Preparing to unpack .../libssl3t64_3.0.13-0ubuntu1_arm64.deb ... 78s Unpacking libssl3t64:arm64 (3.0.13-0ubuntu1) ... 78s Preparing to unpack .../python3.12_3.12.2-4build2_arm64.deb ... 78s Unpacking python3.12 (3.12.2-4build2) over (3.12.2-1) ... 79s Preparing to unpack .../python3.12-minimal_3.12.2-4build2_arm64.deb ... 79s Unpacking python3.12-minimal (3.12.2-4build2) over (3.12.2-1) ... 79s Preparing to unpack .../libpython3.12-minimal_3.12.2-4build2_arm64.deb ... 79s Unpacking libpython3.12-minimal:arm64 (3.12.2-4build2) over (3.12.2-1) ... 79s dpkg: libreadline8:arm64: dependency problems, but removing anyway as you requested: 79s parted depends on libreadline8 (>= 6.0). 79s libpython3.12-stdlib:arm64 depends on libreadline8 (>= 7.0~beta). 79s gpgsm depends on libreadline8 (>= 6.0). 79s gpgconf depends on libreadline8 (>= 6.0). 79s gpg depends on libreadline8 (>= 6.0). 79s gawk depends on libreadline8 (>= 6.0). 79s fdisk depends on libreadline8 (>= 6.0). 79s 79s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74750 files and directories currently installed.) 79s Removing libreadline8:arm64 (8.2-3) ... 79s Selecting previously unselected package libreadline8t64:arm64. 79s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74738 files and directories currently installed.) 79s Preparing to unpack .../libreadline8t64_8.2-3.1_arm64.deb ... 79s Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8 to /lib/aarch64-linux-gnu/libhistory.so.8.usr-is-merged by libreadline8t64' 79s Adding 'diversion of /lib/aarch64-linux-gnu/libhistory.so.8.2 to /lib/aarch64-linux-gnu/libhistory.so.8.2.usr-is-merged by libreadline8t64' 79s Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8 to /lib/aarch64-linux-gnu/libreadline.so.8.usr-is-merged by libreadline8t64' 79s Adding 'diversion of /lib/aarch64-linux-gnu/libreadline.so.8.2 to /lib/aarch64-linux-gnu/libreadline.so.8.2.usr-is-merged by libreadline8t64' 79s Unpacking libreadline8t64:arm64 (8.2-3.1) ... 79s Preparing to unpack .../libpython3.12-stdlib_3.12.2-4build2_arm64.deb ... 79s Unpacking libpython3.12-stdlib:arm64 (3.12.2-4build2) over (3.12.2-1) ... 79s dpkg: libperl5.38:arm64: dependency problems, but removing anyway as you requested: 79s perl depends on libperl5.38 (= 5.38.2-3). 79s 79s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74756 files and directories currently installed.) 79s Removing libperl5.38:arm64 (5.38.2-3) ... 79s dpkg: libdb5.3:arm64: dependency problems, but removing anyway as you requested: 79s libsasl2-modules-db:arm64 depends on libdb5.3. 79s libpam-modules:arm64 depends on libdb5.3. 79s iproute2 depends on libdb5.3. 79s apt-utils depends on libdb5.3. 79s 79s Removing libdb5.3:arm64 (5.3.28+dfsg2-4) ... 80s Selecting previously unselected package libdb5.3t64:arm64. 80s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74221 files and directories currently installed.) 80s Preparing to unpack .../libdb5.3t64_5.3.28+dfsg2-5_arm64.deb ... 80s Unpacking libdb5.3t64:arm64 (5.3.28+dfsg2-5) ... 80s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74227 files and directories currently installed.) 80s Removing libgdbm-compat4:arm64 (1.23-5) ... 80s dpkg: libgdbm6:arm64: dependency problems, but removing anyway as you requested: 80s python3-gdbm:arm64 depends on libgdbm6 (>= 1.16). 80s man-db depends on libgdbm6 (>= 1.16). 80s 80s Removing libgdbm6:arm64 (1.23-5) ... 80s Selecting previously unselected package libgdbm6t64:arm64. 80s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74217 files and directories currently installed.) 80s Preparing to unpack .../libgdbm6t64_1.23-5.1_arm64.deb ... 80s Unpacking libgdbm6t64:arm64 (1.23-5.1) ... 80s Selecting previously unselected package libgdbm-compat4t64:arm64. 80s Preparing to unpack .../libgdbm-compat4t64_1.23-5.1_arm64.deb ... 80s Unpacking libgdbm-compat4t64:arm64 (1.23-5.1) ... 80s Selecting previously unselected package libperl5.38t64:arm64. 80s Preparing to unpack .../libperl5.38t64_5.38.2-3.2_arm64.deb ... 80s Unpacking libperl5.38t64:arm64 (5.38.2-3.2) ... 80s Preparing to unpack .../perl_5.38.2-3.2_arm64.deb ... 80s Unpacking perl (5.38.2-3.2) over (5.38.2-3) ... 80s Preparing to unpack .../perl-base_5.38.2-3.2_arm64.deb ... 80s Unpacking perl-base (5.38.2-3.2) over (5.38.2-3) ... 80s Setting up perl-base (5.38.2-3.2) ... 80s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74758 files and directories currently installed.) 80s Preparing to unpack .../perl-modules-5.38_5.38.2-3.2_all.deb ... 80s Unpacking perl-modules-5.38 (5.38.2-3.2) over (5.38.2-3) ... 81s Preparing to unpack .../readline-common_8.2-3.1_all.deb ... 81s Unpacking readline-common (8.2-3.1) over (8.2-3) ... 81s Preparing to unpack .../klibc-utils_2.0.13-4_arm64.deb ... 81s Unpacking klibc-utils (2.0.13-4) over (2.0.13-2ubuntu1) ... 81s Preparing to unpack .../libklibc_2.0.13-4_arm64.deb ... 81s Unpacking libklibc:arm64 (2.0.13-4) over (2.0.13-2ubuntu1) ... 81s Setting up libgdbm6t64:arm64 (1.23-5.1) ... 81s Setting up libgdbm-compat4t64:arm64 (1.23-5.1) ... 81s Setting up libssl3t64:arm64 (3.0.13-0ubuntu1) ... 81s Setting up libpython3.12-minimal:arm64 (3.12.2-4build2) ... 81s Setting up libklibc:arm64 (2.0.13-4) ... 81s Setting up perl-modules-5.38 (5.38.2-3.2) ... 81s Setting up libdb5.3t64:arm64 (5.3.28+dfsg2-5) ... 81s Setting up libperl5.38t64:arm64 (5.38.2-3.2) ... 81s Setting up klibc-utils (2.0.13-4) ... 81s Setting up openssl (3.0.13-0ubuntu1) ... 81s Setting up readline-common (8.2-3.1) ... 81s Setting up python3.12-minimal (3.12.2-4build2) ... 82s Setting up perl (5.38.2-3.2) ... 82s Setting up libreadline8t64:arm64 (8.2-3.1) ... 82s Setting up libpython3.12-stdlib:arm64 (3.12.2-4build2) ... 82s Setting up python3.12 (3.12.2-4build2) ... 84s Processing triggers for systemd (255.2-3ubuntu2) ... 84s Processing triggers for man-db (2.12.0-3) ... 85s Processing triggers for install-info (7.1-3) ... 85s Processing triggers for initramfs-tools (0.142ubuntu20) ... 85s update-initramfs: Generating /boot/initrd.img-6.8.0-11-generic 85s W: No lz4 in /usr/bin:/sbin:/bin, using gzip 104s System running in EFI mode, skipping. 104s Processing triggers for libc-bin (2.39-0ubuntu2) ... 104s Reading package lists... 104s Building dependency tree... 104s Reading state information... 105s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 106s sh: Attempting to set up Debian/Ubuntu apt sources automatically 106s sh: Distribution appears to be Ubuntu 107s Reading package lists... 107s Building dependency tree... 107s Reading state information... 108s eatmydata is already the newest version (131-1). 108s dbus is already the newest version (1.14.10-4ubuntu1). 108s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 108s Reading package lists... 108s Building dependency tree... 108s Reading state information... 109s rng-tools-debian is already the newest version (2.4). 109s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 109s Reading package lists... 109s Building dependency tree... 109s Reading state information... 110s haveged is already the newest version (1.9.14-1ubuntu1). 110s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 110s Reading package lists... 110s Building dependency tree... 110s Reading state information... 110s The following packages will be REMOVED: 110s cloud-init* python3-configobj* python3-debconf* 111s 0 upgraded, 0 newly installed, 3 to remove and 0 not upgraded. 111s After this operation, 3248 kB disk space will be freed. 111s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74758 files and directories currently installed.) 111s Removing cloud-init (24.1-0ubuntu1) ... 112s Removing python3-configobj (5.0.8-3) ... 112s Removing python3-debconf (1.5.86) ... 112s Processing triggers for man-db (2.12.0-3) ... 113s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74369 files and directories currently installed.) 113s Purging configuration files for cloud-init (24.1-0ubuntu1) ... 113s dpkg: warning: while removing cloud-init, directory '/etc/cloud/cloud.cfg.d' not empty so not removed 113s Processing triggers for rsyslog (8.2312.0-3ubuntu3) ... 114s Reading package lists... 114s Building dependency tree... 114s Reading state information... 115s linux-generic is already the newest version (6.8.0-11.11+1). 115s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 115s Hit:1 http://ftpmaster.internal/ubuntu noble InRelease 115s Hit:2 http://ftpmaster.internal/ubuntu noble-updates InRelease 115s Hit:3 http://ftpmaster.internal/ubuntu noble-security InRelease 115s Hit:4 http://ftpmaster.internal/ubuntu noble-proposed InRelease 117s Reading package lists... 117s Reading package lists... 117s Building dependency tree... 117s Reading state information... 117s Calculating upgrade... 118s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 118s Reading package lists... 118s Building dependency tree... 118s Reading state information... 119s 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 119s autopkgtest [17:39:09]: rebooting testbed after setup commands that affected boot 332s autopkgtest [17:42:42]: testbed running kernel: Linux 6.8.0-11-generic #11-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 14 02:53:31 UTC 2024 332s autopkgtest [17:42:42]: testbed dpkg architecture: arm64 334s autopkgtest [17:42:44]: @@@@@@@@@@@@@@@@@@@@ apt-source slony1-2 336s Get:1 http://ftpmaster.internal/ubuntu noble/universe slony1-2 2.2.11-3 (dsc) [2413 B] 336s Get:2 http://ftpmaster.internal/ubuntu noble/universe slony1-2 2.2.11-3 (tar) [1465 kB] 336s Get:3 http://ftpmaster.internal/ubuntu noble/universe slony1-2 2.2.11-3 (diff) [16.8 kB] 337s gpgv: Signature made Fri Sep 22 12:00:13 2023 UTC 337s gpgv: using RSA key 5C48FE6157F49179597087C64C5A6BAB12D2A7AE 337s gpgv: Can't check signature: No public key 337s dpkg-source: warning: cannot verify inline signature for ./slony1-2_2.2.11-3.dsc: no acceptable signature found 337s autopkgtest [17:42:47]: testing package slony1-2 version 2.2.11-3 337s autopkgtest [17:42:47]: build not needed 338s autopkgtest [17:42:48]: test load-functions: preparing testbed 342s Reading package lists... 342s Building dependency tree... 342s Reading state information... 342s Correcting dependencies...Starting pkgProblemResolver with broken count: 0 342s Starting 2 pkgProblemResolver with broken count: 0 342s Done 343s Done 343s Starting pkgProblemResolver with broken count: 0 343s Starting 2 pkgProblemResolver with broken count: 0 343s Done 344s The following additional packages will be installed: 344s libjson-perl libllvm17t64 libpq5 libxslt1.1 postgresql-16 344s postgresql-16-slony1-2 postgresql-client-16 postgresql-client-common 344s postgresql-common slony1-2-bin slony1-2-doc ssl-cert 344s Suggested packages: 344s postgresql-doc-16 libpg-perl 344s Recommended packages: 344s libjson-xs-perl libdbd-pg-perl 344s The following NEW packages will be installed: 344s libjson-perl libllvm17t64 libpq5 libxslt1.1 postgresql-16 344s postgresql-16-slony1-2 postgresql-client-16 postgresql-client-common 344s postgresql-common slony1-2-bin slony1-2-doc ssl-cert 344s 0 upgraded, 12 newly installed, 0 to remove and 0 not upgraded. 344s 1 not fully installed or removed. 344s Need to get 42.7 MB of archives. 344s After this operation, 178 MB of additional disk space will be used. 344s Get:1 http://ftpmaster.internal/ubuntu noble/main arm64 libjson-perl all 4.10000-1 [81.9 kB] 344s Get:2 http://ftpmaster.internal/ubuntu noble/main arm64 postgresql-client-common all 257 [36.2 kB] 344s Get:3 http://ftpmaster.internal/ubuntu noble/main arm64 ssl-cert all 1.1.2ubuntu1 [17.8 kB] 344s Get:4 http://ftpmaster.internal/ubuntu noble/main arm64 postgresql-common all 257 [162 kB] 344s Get:5 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libpq5 arm64 16.2-1ubuntu2 [137 kB] 344s Get:6 http://ftpmaster.internal/ubuntu noble/universe arm64 slony1-2-bin arm64 2.2.11-3 [221 kB] 344s Get:7 http://ftpmaster.internal/ubuntu noble/universe arm64 slony1-2-doc all 2.2.11-3 [328 kB] 344s Get:8 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 postgresql-client-16 arm64 16.2-1ubuntu2 [1261 kB] 344s Get:9 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 libllvm17t64 arm64 1:17.0.6-8ubuntu1.1 [25.0 MB] 345s Get:10 http://ftpmaster.internal/ubuntu noble/main arm64 libxslt1.1 arm64 1.1.35-1 [161 kB] 345s Get:11 http://ftpmaster.internal/ubuntu noble-proposed/main arm64 postgresql-16 arm64 16.2-1ubuntu2 [15.2 MB] 345s Get:12 http://ftpmaster.internal/ubuntu noble/universe arm64 postgresql-16-slony1-2 arm64 2.2.11-3 [19.6 kB] 346s Preconfiguring packages ... 346s Fetched 42.7 MB in 2s (26.8 MB/s) 346s Selecting previously unselected package libjson-perl. 346s (Reading database ... (Reading database ... 5% (Reading database ... 10% (Reading database ... 15% (Reading database ... 20% (Reading database ... 25% (Reading database ... 30% (Reading database ... 35% (Reading database ... 40% (Reading database ... 45% (Reading database ... 50% (Reading database ... 55% (Reading database ... 60% (Reading database ... 65% (Reading database ... 70% (Reading database ... 75% (Reading database ... 80% (Reading database ... 85% (Reading database ... 90% (Reading database ... 95% (Reading database ... 100% (Reading database ... 74314 files and directories currently installed.) 346s Preparing to unpack .../00-libjson-perl_4.10000-1_all.deb ... 346s Unpacking libjson-perl (4.10000-1) ... 346s Selecting previously unselected package postgresql-client-common. 346s Preparing to unpack .../01-postgresql-client-common_257_all.deb ... 346s Unpacking postgresql-client-common (257) ... 346s Selecting previously unselected package ssl-cert. 346s Preparing to unpack .../02-ssl-cert_1.1.2ubuntu1_all.deb ... 346s Unpacking ssl-cert (1.1.2ubuntu1) ... 346s Selecting previously unselected package postgresql-common. 346s Preparing to unpack .../03-postgresql-common_257_all.deb ... 346s Adding 'diversion of /usr/bin/pg_config to /usr/bin/pg_config.libpq-dev by postgresql-common' 346s Unpacking postgresql-common (257) ... 346s Selecting previously unselected package libpq5:arm64. 346s Preparing to unpack .../04-libpq5_16.2-1ubuntu2_arm64.deb ... 346s Unpacking libpq5:arm64 (16.2-1ubuntu2) ... 346s Selecting previously unselected package slony1-2-bin. 346s Preparing to unpack .../05-slony1-2-bin_2.2.11-3_arm64.deb ... 346s Unpacking slony1-2-bin (2.2.11-3) ... 346s Selecting previously unselected package slony1-2-doc. 346s Preparing to unpack .../06-slony1-2-doc_2.2.11-3_all.deb ... 346s Unpacking slony1-2-doc (2.2.11-3) ... 346s Selecting previously unselected package postgresql-client-16. 346s Preparing to unpack .../07-postgresql-client-16_16.2-1ubuntu2_arm64.deb ... 346s Unpacking postgresql-client-16 (16.2-1ubuntu2) ... 346s Selecting previously unselected package libllvm17t64:arm64. 346s Preparing to unpack .../08-libllvm17t64_1%3a17.0.6-8ubuntu1.1_arm64.deb ... 346s Unpacking libllvm17t64:arm64 (1:17.0.6-8ubuntu1.1) ... 347s Selecting previously unselected package libxslt1.1:arm64. 347s Preparing to unpack .../09-libxslt1.1_1.1.35-1_arm64.deb ... 347s Unpacking libxslt1.1:arm64 (1.1.35-1) ... 347s Selecting previously unselected package postgresql-16. 347s Preparing to unpack .../10-postgresql-16_16.2-1ubuntu2_arm64.deb ... 347s Unpacking postgresql-16 (16.2-1ubuntu2) ... 348s Selecting previously unselected package postgresql-16-slony1-2. 348s Preparing to unpack .../11-postgresql-16-slony1-2_2.2.11-3_arm64.deb ... 348s Unpacking postgresql-16-slony1-2 (2.2.11-3) ... 348s Setting up postgresql-client-common (257) ... 348s Setting up libpq5:arm64 (16.2-1ubuntu2) ... 348s Setting up libllvm17t64:arm64 (1:17.0.6-8ubuntu1.1) ... 348s Setting up ssl-cert (1.1.2ubuntu1) ... 349s Created symlink /etc/systemd/system/multi-user.target.wants/ssl-cert.service → /usr/lib/systemd/system/ssl-cert.service. 349s Setting up libjson-perl (4.10000-1) ... 349s Setting up libxslt1.1:arm64 (1.1.35-1) ... 349s Setting up slony1-2-doc (2.2.11-3) ... 349s Setting up postgresql-client-16 (16.2-1ubuntu2) ... 350s update-alternatives: using /usr/share/postgresql/16/man/man1/psql.1.gz to provide /usr/share/man/man1/psql.1.gz (psql.1.gz) in auto mode 350s Setting up postgresql-common (257) ... 351s 351s Creating config file /etc/postgresql-common/createcluster.conf with new version 351s Building PostgreSQL dictionaries from installed myspell/hunspell packages... 351s Removing obsolete dictionary files: 352s Created symlink /etc/systemd/system/multi-user.target.wants/postgresql.service → /usr/lib/systemd/system/postgresql.service. 352s Setting up slony1-2-bin (2.2.11-3) ... 353s Setting up postgresql-16 (16.2-1ubuntu2) ... 353s Creating new PostgreSQL cluster 16/main ... 353s /usr/lib/postgresql/16/bin/initdb -D /var/lib/postgresql/16/main --auth-local peer --auth-host scram-sha-256 --no-instructions 353s The files belonging to this database system will be owned by user "postgres". 353s This user must also own the server process. 353s 353s The database cluster will be initialized with locale "C.UTF-8". 353s The default database encoding has accordingly been set to "UTF8". 353s The default text search configuration will be set to "english". 353s 353s Data page checksums are disabled. 353s 353s fixing permissions on existing directory /var/lib/postgresql/16/main ... ok 353s creating subdirectories ... ok 353s selecting dynamic shared memory implementation ... posix 353s selecting default max_connections ... 100 353s selecting default shared_buffers ... 128MB 353s selecting default time zone ... Etc/UTC 353s creating configuration files ... ok 354s running bootstrap script ... ok 354s performing post-bootstrap initialization ... ok 354s syncing data to disk ... ok 358s Setting up postgresql-16-slony1-2 (2.2.11-3) ... 358s Setting up autopkgtest-satdep (0) ... 358s Processing triggers for man-db (2.12.0-3) ... 359s Processing triggers for libc-bin (2.39-0ubuntu2) ... 363s (Reading database ... 76643 files and directories currently installed.) 363s Removing autopkgtest-satdep (0) ... 364s autopkgtest [17:43:14]: test load-functions: [----------------------- 364s ### PostgreSQL 16 psql ### 364s Creating new PostgreSQL cluster 16/regress ... 367s create table public.sl_node ( 367s no_id int4, 367s no_active bool, 367s no_comment text, 367s no_failed bool, 367s CONSTRAINT "sl_node-pkey" 367s PRIMARY KEY (no_id) 367s ) WITHOUT OIDS; 367s CREATE TABLE 367s comment on table public.sl_node is 'Holds the list of nodes associated with this namespace.'; 367s COMMENT 367s comment on column public.sl_node.no_id is 'The unique ID number for the node'; 367s COMMENT 367s comment on column public.sl_node.no_active is 'Is the node active in replication yet?'; 367s COMMENT 367s comment on column public.sl_node.no_comment is 'A human-oriented description of the node'; 367s COMMENT 367s create table public.sl_nodelock ( 367s nl_nodeid int4, 367s nl_conncnt serial, 367s nl_backendpid int4, 367s CONSTRAINT "sl_nodelock-pkey" 367s PRIMARY KEY (nl_nodeid, nl_conncnt) 367s ) WITHOUT OIDS; 367s CREATE TABLE 367s comment on table public.sl_nodelock is 'Used to prevent multiple slon instances and to identify the backends to kill in terminateNodeConnections().'; 367s COMMENT 368s comment on column public.sl_nodelock.nl_nodeid is 'Clients node_id'; 368s COMMENT 368s comment on column public.sl_nodelock.nl_conncnt is 'Clients connection number'; 368s COMMENT 368s comment on column public.sl_nodelock.nl_backendpid is 'PID of database backend owning this lock'; 368s COMMENT 368s create table public.sl_set ( 368s set_id int4, 368s set_origin int4, 368s set_locked bigint, 368s set_comment text, 368s CONSTRAINT "sl_set-pkey" 368s PRIMARY KEY (set_id), 368s CONSTRAINT "set_origin-no_id-ref" 368s FOREIGN KEY (set_origin) 368s REFERENCES public.sl_node (no_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_set is 'Holds definitions of replication sets.'; 368s COMMENT 368s comment on column public.sl_set.set_id is 'A unique ID number for the set.'; 368s COMMENT 368s comment on column public.sl_set.set_origin is 368s 'The ID number of the source node for the replication set.'; 368s COMMENT 368s comment on column public.sl_set.set_locked is 'Transaction ID where the set was locked.'; 368s COMMENT 368s comment on column public.sl_set.set_comment is 'A human-oriented description of the set.'; 368s COMMENT 368s create table public.sl_setsync ( 368s ssy_setid int4, 368s ssy_origin int4, 368s ssy_seqno int8, 368s ssy_snapshot "pg_catalog".txid_snapshot, 368s ssy_action_list text, 368s CONSTRAINT "sl_setsync-pkey" 368s PRIMARY KEY (ssy_setid), 368s CONSTRAINT "ssy_setid-set_id-ref" 368s FOREIGN KEY (ssy_setid) 368s REFERENCES public.sl_set (set_id), 368s CONSTRAINT "ssy_origin-no_id-ref" 368s FOREIGN KEY (ssy_origin) 368s REFERENCES public.sl_node (no_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_setsync is 'SYNC information'; 368s COMMENT 368s comment on column public.sl_setsync.ssy_setid is 'ID number of the replication set'; 368s COMMENT 368s comment on column public.sl_setsync.ssy_origin is 'ID number of the node'; 368s COMMENT 368s comment on column public.sl_setsync.ssy_seqno is 'Slony-I sequence number'; 368s COMMENT 368s comment on column public.sl_setsync.ssy_snapshot is 'TXID in provider system seen by the event'; 368s COMMENT 368s comment on column public.sl_setsync.ssy_action_list is 'action list used during the subscription process. At the time a subscriber copies over data from the origin, it sees all tables in a state somewhere between two SYNC events. Therefore this list must contains all log_actionseqs that are visible at that time, whose operations have therefore already been included in the data copied at the time the initial data copy is done. Those actions may therefore be filtered out of the first SYNC done after subscribing.'; 368s COMMENT 368s create table public.sl_table ( 368s tab_id int4, 368s tab_reloid oid UNIQUE NOT NULL, 368s tab_relname name NOT NULL, 368s tab_nspname name NOT NULL, 368s tab_set int4, 368s tab_idxname name NOT NULL, 368s tab_altered boolean NOT NULL, 368s tab_comment text, 368s CONSTRAINT "sl_table-pkey" 368s PRIMARY KEY (tab_id), 368s CONSTRAINT "tab_set-set_id-ref" 368s FOREIGN KEY (tab_set) 368s REFERENCES public.sl_set (set_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_table is 'Holds information about the tables being replicated.'; 368s COMMENT 368s comment on column public.sl_table.tab_id is 'Unique key for Slony-I to use to identify the table'; 368s COMMENT 368s comment on column public.sl_table.tab_reloid is 'The OID of the table in pg_catalog.pg_class.oid'; 368s COMMENT 368s comment on column public.sl_table.tab_relname is 'The name of the table in pg_catalog.pg_class.relname used to recover from a dump/restore cycle'; 368s COMMENT 368s comment on column public.sl_table.tab_nspname is 'The name of the schema in pg_catalog.pg_namespace.nspname used to recover from a dump/restore cycle'; 368s COMMENT 368s comment on column public.sl_table.tab_set is 'ID of the replication set the table is in'; 368s COMMENT 368s comment on column public.sl_table.tab_idxname is 'The name of the primary index of the table'; 368s COMMENT 368s comment on column public.sl_table.tab_altered is 'Has the table been modified for replication?'; 368s COMMENT 368s comment on column public.sl_table.tab_comment is 'Human-oriented description of the table'; 368s COMMENT 368s create table public.sl_sequence ( 368s seq_id int4, 368s seq_reloid oid UNIQUE NOT NULL, 368s seq_relname name NOT NULL, 368s seq_nspname name NOT NULL, 368s seq_set int4, 368s seq_comment text, 368s CONSTRAINT "sl_sequence-pkey" 368s PRIMARY KEY (seq_id), 368s CONSTRAINT "seq_set-set_id-ref" 368s FOREIGN KEY (seq_set) 368s REFERENCES public.sl_set (set_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_sequence is 'Similar to sl_table, each entry identifies a sequence being replicated.'; 368s COMMENT 368s comment on column public.sl_sequence.seq_id is 'An internally-used ID for Slony-I to use in its sequencing of updates'; 368s COMMENT 368s comment on column public.sl_sequence.seq_reloid is 'The OID of the sequence object'; 368s COMMENT 368s comment on column public.sl_sequence.seq_relname is 'The name of the sequence in pg_catalog.pg_class.relname used to recover from a dump/restore cycle'; 368s COMMENT 368s comment on column public.sl_sequence.seq_nspname is 'The name of the schema in pg_catalog.pg_namespace.nspname used to recover from a dump/restore cycle'; 368s COMMENT 368s comment on column public.sl_sequence.seq_set is 'Indicates which replication set the object is in'; 368s COMMENT 368s comment on column public.sl_sequence.seq_comment is 'A human-oriented comment'; 368s COMMENT 368s create table public.sl_path ( 368s pa_server int4, 368s pa_client int4, 368s pa_conninfo text NOT NULL, 368s pa_connretry int4, 368s CONSTRAINT "sl_path-pkey" 368s PRIMARY KEY (pa_server, pa_client), 368s CONSTRAINT "pa_server-no_id-ref" 368s FOREIGN KEY (pa_server) 368s REFERENCES public.sl_node (no_id), 368s CONSTRAINT "pa_client-no_id-ref" 368s FOREIGN KEY (pa_client) 368s REFERENCES public.sl_node (no_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_path is 'Holds connection information for the paths between nodes, and the synchronisation delay'; 368s COMMENT 368s comment on column public.sl_path.pa_server is 'The Node ID # (from sl_node.no_id) of the data source'; 368s COMMENT 368s comment on column public.sl_path.pa_client is 'The Node ID # (from sl_node.no_id) of the data target'; 368s COMMENT 368s comment on column public.sl_path.pa_conninfo is 'The PostgreSQL connection string used to connect to the source node.'; 368s COMMENT 368s comment on column public.sl_path.pa_connretry is 'The synchronisation delay, in seconds'; 368s COMMENT 368s create table public.sl_listen ( 368s li_origin int4, 368s li_provider int4, 368s li_receiver int4, 368s CONSTRAINT "sl_listen-pkey" 368s PRIMARY KEY (li_origin, li_provider, li_receiver), 368s CONSTRAINT "li_origin-no_id-ref" 368s FOREIGN KEY (li_origin) 368s REFERENCES public.sl_node (no_id), 368s CONSTRAINT "sl_listen-sl_path-ref" 368s FOREIGN KEY (li_provider, li_receiver) 368s REFERENCES public.sl_path (pa_server, pa_client) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_listen is 'Indicates how nodes listen to events from other nodes in the Slony-I network.'; 368s COMMENT 368s comment on column public.sl_listen.li_origin is 'The ID # (from sl_node.no_id) of the node this listener is operating on'; 368s COMMENT 368s comment on column public.sl_listen.li_provider is 'The ID # (from sl_node.no_id) of the source node for this listening event'; 368s COMMENT 368s comment on column public.sl_listen.li_receiver is 'The ID # (from sl_node.no_id) of the target node for this listening event'; 368s COMMENT 368s create table public.sl_subscribe ( 368s sub_set int4, 368s sub_provider int4, 368s sub_receiver int4, 368s sub_forward bool, 368s sub_active bool, 368s CONSTRAINT "sl_subscribe-pkey" 368s PRIMARY KEY (sub_receiver, sub_set), 368s CONSTRAINT "sl_subscribe-sl_path-ref" 368s FOREIGN KEY (sub_provider, sub_receiver) 368s REFERENCES public.sl_path (pa_server, pa_client), 368s CONSTRAINT "sub_set-set_id-ref" 368s FOREIGN KEY (sub_set) 368s REFERENCES public.sl_set (set_id) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_subscribe is 'Holds a list of subscriptions on sets'; 368s COMMENT 368s comment on column public.sl_subscribe.sub_set is 'ID # (from sl_set) of the set being subscribed to'; 368s COMMENT 368s comment on column public.sl_subscribe.sub_provider is 'ID# (from sl_node) of the node providing data'; 368s COMMENT 368s comment on column public.sl_subscribe.sub_receiver is 'ID# (from sl_node) of the node receiving data from the provider'; 368s COMMENT 368s comment on column public.sl_subscribe.sub_forward is 'Does this provider keep data in sl_log_1/sl_log_2 to allow it to be a provider for other nodes?'; 368s COMMENT 368s comment on column public.sl_subscribe.sub_active is 'Has this subscription been activated? This is not set on the subscriber until AFTER the subscriber has received COPY data from the provider'; 368s COMMENT 368s create table public.sl_event ( 368s ev_origin int4, 368s ev_seqno int8, 368s ev_timestamp timestamptz, 368s ev_snapshot "pg_catalog".txid_snapshot, 368s ev_type text, 368s ev_data1 text, 368s ev_data2 text, 368s ev_data3 text, 368s ev_data4 text, 368s ev_data5 text, 368s ev_data6 text, 368s ev_data7 text, 368s ev_data8 text, 368s CONSTRAINT "sl_event-pkey" 368s PRIMARY KEY (ev_origin, ev_seqno) 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_event is 'Holds information about replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_confirm table.'; 368s COMMENT 368s comment on column public.sl_event.ev_origin is 'The ID # (from sl_node.no_id) of the source node for this event'; 368s COMMENT 368s comment on column public.sl_event.ev_seqno is 'The ID # for the event'; 368s COMMENT 368s comment on column public.sl_event.ev_timestamp is 'When this event record was created'; 368s COMMENT 368s comment on column public.sl_event.ev_snapshot is 'TXID snapshot on provider node for this event'; 368s COMMENT 368s comment on column public.sl_event.ev_seqno is 'The ID # for the event'; 368s COMMENT 368s comment on column public.sl_event.ev_type is 'The type of event this record is for. 368s SYNC = Synchronise 368s STORE_NODE = 368s ENABLE_NODE = 368s DROP_NODE = 368s STORE_PATH = 368s DROP_PATH = 368s STORE_LISTEN = 368s DROP_LISTEN = 368s STORE_SET = 368s DROP_SET = 368s MERGE_SET = 368s SET_ADD_TABLE = 368s SET_ADD_SEQUENCE = 368s STORE_TRIGGER = 368s DROP_TRIGGER = 368s MOVE_SET = 368s ACCEPT_SET = 368s SET_DROP_TABLE = 368s SET_DROP_SEQUENCE = 368s SET_MOVE_TABLE = 368s SET_MOVE_SEQUENCE = 368s FAILOVER_SET = 368s SUBSCRIBE_SET = 368s ENABLE_SUBSCRIPTION = 368s UNSUBSCRIBE_SET = 368s DDL_SCRIPT = 368s ADJUST_SEQ = 368s RESET_CONFIG = 368s '; 368s COMMENT 368s comment on column public.sl_event.ev_data1 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data2 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data3 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data4 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data5 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data6 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data7 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s comment on column public.sl_event.ev_data8 is 'Data field containing an argument needed to process the event'; 368s COMMENT 368s create table public.sl_confirm ( 368s con_origin int4, 368s con_received int4, 368s con_seqno int8, 368s con_timestamp timestamptz DEFAULT timeofday()::timestamptz 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_confirm is 'Holds confirmation of replication events. After a period of time, Slony removes old confirmed events from both this table and the sl_event table.'; 368s COMMENT 368s comment on column public.sl_confirm.con_origin is 'The ID # (from sl_node.no_id) of the source node for this event'; 368s COMMENT 368s comment on column public.sl_confirm.con_seqno is 'The ID # for the event'; 368s COMMENT 368s comment on column public.sl_confirm.con_timestamp is 'When this event was confirmed'; 368s COMMENT 368s create index sl_confirm_idx1 on public.sl_confirm 368s (con_origin, con_received, con_seqno); 368s CREATE INDEX 368s create index sl_confirm_idx2 on public.sl_confirm 368s (con_received, con_seqno); 368s CREATE INDEX 368s create table public.sl_seqlog ( 368s seql_seqid int4, 368s seql_origin int4, 368s seql_ev_seqno int8, 368s seql_last_value int8 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_seqlog is 'Log of Sequence updates'; 368s COMMENT 368s comment on column public.sl_seqlog.seql_seqid is 'Sequence ID'; 368s COMMENT 368s comment on column public.sl_seqlog.seql_origin is 'Publisher node at which the sequence originates'; 368s COMMENT 368s comment on column public.sl_seqlog.seql_ev_seqno is 'Slony-I Event with which this sequence update is associated'; 368s COMMENT 368s comment on column public.sl_seqlog.seql_last_value is 'Last value published for this sequence'; 368s COMMENT 368s create index sl_seqlog_idx on public.sl_seqlog 368s (seql_origin, seql_ev_seqno, seql_seqid); 368s CREATE INDEX 368s create function public.sequenceLastValue(p_seqname text) returns int8 368s as $$ 368s declare 368s v_seq_row record; 368s begin 368s for v_seq_row in execute 'select last_value from ' || public.slon_quote_input(p_seqname) 368s loop 368s return v_seq_row.last_value; 368s end loop; 368s 368s -- not reached 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.sequenceLastValue(p_seqname text) is 368s 'sequenceLastValue(p_seqname) 368s 368s Utility function used in sl_seqlastvalue view to compactly get the 368s last value from the requested sequence.'; 368s COMMENT 368s create table public.sl_log_1 ( 368s log_origin int4, 368s log_txid bigint, 368s log_tableid int4, 368s log_actionseq int8, 368s log_tablenspname text, 368s log_tablerelname text, 368s log_cmdtype "char", 368s log_cmdupdncols int4, 368s log_cmdargs text[] 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s create index sl_log_1_idx1 on public.sl_log_1 368s (log_origin, log_txid, log_actionseq); 368s CREATE INDEX 368s comment on table public.sl_log_1 is 'Stores each change to be propagated to subscriber nodes'; 368s COMMENT 368s comment on column public.sl_log_1.log_origin is 'Origin node from which the change came'; 368s COMMENT 368s comment on column public.sl_log_1.log_txid is 'Transaction ID on the origin node'; 368s COMMENT 368s comment on column public.sl_log_1.log_tableid is 'The table ID (from sl_table.tab_id) that this log entry is to affect'; 368s COMMENT 368s comment on column public.sl_log_1.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s COMMENT 368s comment on column public.sl_log_1.log_tablenspname is 'The schema name of the table affected'; 368s COMMENT 368s comment on column public.sl_log_1.log_tablerelname is 'The table name of the table affected'; 368s COMMENT 368s comment on column public.sl_log_1.log_cmdtype is 'Replication action to take. U = Update, I = Insert, D = DELETE, T = TRUNCATE'; 368s COMMENT 368s comment on column public.sl_log_1.log_cmdupdncols is 'For cmdtype=U the number of updated columns in cmdargs'; 368s COMMENT 368s comment on column public.sl_log_1.log_cmdargs is 'The data needed to perform the log action on the replica'; 368s COMMENT 368s create table public.sl_log_2 ( 368s log_origin int4, 368s log_txid bigint, 368s log_tableid int4, 368s log_actionseq int8, 368s log_tablenspname text, 368s log_tablerelname text, 368s log_cmdtype "char", 368s log_cmdupdncols int4, 368s log_cmdargs text[] 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s create index sl_log_2_idx1 on public.sl_log_2 368s (log_origin, log_txid, log_actionseq); 368s CREATE INDEX 368s comment on table public.sl_log_2 is 'Stores each change to be propagated to subscriber nodes'; 368s COMMENT 368s comment on column public.sl_log_2.log_origin is 'Origin node from which the change came'; 368s COMMENT 368s comment on column public.sl_log_2.log_txid is 'Transaction ID on the origin node'; 368s COMMENT 368s comment on column public.sl_log_2.log_tableid is 'The table ID (from sl_table.tab_id) that this log entry is to affect'; 368s COMMENT 368s comment on column public.sl_log_2.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s COMMENT 368s comment on column public.sl_log_2.log_tablenspname is 'The schema name of the table affected'; 368s COMMENT 368s comment on column public.sl_log_2.log_tablerelname is 'The table name of the table affected'; 368s COMMENT 368s comment on column public.sl_log_2.log_cmdtype is 'Replication action to take. U = Update, I = Insert, D = DELETE, T = TRUNCATE'; 368s COMMENT 368s comment on column public.sl_log_2.log_cmdupdncols is 'For cmdtype=U the number of updated columns in cmdargs'; 368s COMMENT 368s comment on column public.sl_log_2.log_cmdargs is 'The data needed to perform the log action on the replica'; 368s COMMENT 368s create table public.sl_log_script ( 368s log_origin int4, 368s log_txid bigint, 368s log_actionseq int8, 368s log_cmdtype "char", 368s log_cmdargs text[] 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s create index sl_log_script_idx1 on public.sl_log_script 368s (log_origin, log_txid, log_actionseq); 368s CREATE INDEX 368s comment on table public.sl_log_script is 'Captures SQL script queries to be propagated to subscriber nodes'; 368s COMMENT 368s comment on column public.sl_log_script.log_origin is 'Origin name from which the change came'; 368s COMMENT 368s comment on column public.sl_log_script.log_txid is 'Transaction ID on the origin node'; 368s COMMENT 368s comment on column public.sl_log_script.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s COMMENT 368s comment on column public.sl_log_2.log_cmdtype is 'Replication action to take. S = Script statement, s = Script complete'; 368s COMMENT 368s comment on column public.sl_log_script.log_cmdargs is 'The DDL statement, optionally followed by selected nodes to execute it on.'; 368s COMMENT 368s create table public.sl_registry ( 368s reg_key text primary key, 368s reg_int4 int4, 368s reg_text text, 368s reg_timestamp timestamptz 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s comment on table public.sl_registry is 'Stores miscellaneous runtime data'; 368s COMMENT 368s comment on column public.sl_registry.reg_key is 'Unique key of the runtime option'; 368s COMMENT 368s comment on column public.sl_registry.reg_int4 is 'Option value if type int4'; 368s COMMENT 368s comment on column public.sl_registry.reg_text is 'Option value if type text'; 368s COMMENT 368s comment on column public.sl_registry.reg_timestamp is 'Option value if type timestamp'; 368s COMMENT 368s create table public.sl_apply_stats ( 368s as_origin int4, 368s as_num_insert int8, 368s as_num_update int8, 368s as_num_delete int8, 368s as_num_truncate int8, 368s as_num_script int8, 368s as_num_total int8, 368s as_duration interval, 368s as_apply_first timestamptz, 368s as_apply_last timestamptz, 368s as_cache_prepare int8, 368s as_cache_hit int8, 368s as_cache_evict int8, 368s as_cache_prepare_max int8 368s ) WITHOUT OIDS; 368s CREATE TABLE 368s create index sl_apply_stats_idx1 on public.sl_apply_stats 368s (as_origin); 368s CREATE INDEX 368s comment on table public.sl_apply_stats is 'Local SYNC apply statistics (running totals)'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_origin is 'Origin of the SYNCs'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_insert is 'Number of INSERT operations performed'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_update is 'Number of UPDATE operations performed'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_delete is 'Number of DELETE operations performed'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_truncate is 'Number of TRUNCATE operations performed'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_script is 'Number of DDL operations performed'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_num_total is 'Total number of operations'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_duration is 'Processing time'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_apply_first is 'Timestamp of first recorded SYNC'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_apply_last is 'Timestamp of most recent recorded SYNC'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_cache_evict is 'Number of apply query cache evict operations'; 368s COMMENT 368s comment on column public.sl_apply_stats.as_cache_prepare_max is 'Maximum number of apply queries prepared in one SYNC group'; 368s COMMENT 368s create view public.sl_seqlastvalue as 368s select SQ.seq_id, SQ.seq_set, SQ.seq_reloid, 368s S.set_origin as seq_origin, 368s public.sequenceLastValue( 368s "pg_catalog".quote_ident(PGN.nspname) || '.' || 368s "pg_catalog".quote_ident(PGC.relname)) as seq_last_value 368s from public.sl_sequence SQ, public.sl_set S, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where S.set_id = SQ.seq_set 368s and PGC.oid = SQ.seq_reloid and PGN.oid = PGC.relnamespace; 368s CREATE VIEW 368s create view public.sl_failover_targets as 368s select set_id, 368s set_origin as set_origin, 368s sub1.sub_receiver as backup_id 368s FROM 368s public.sl_subscribe sub1 368s ,public.sl_set set1 368s where 368s sub1.sub_set=set_id 368s and sub1.sub_forward=true 368s --exclude candidates where the set_origin 368s --has a path a node but the failover 368s --candidate has no path to that node 368s and sub1.sub_receiver not in 368s (select p1.pa_client from 368s public.sl_path p1 368s left outer join public.sl_path p2 on 368s (p2.pa_client=p1.pa_client 368s and p2.pa_server=sub1.sub_receiver) 368s where p2.pa_client is null 368s and p1.pa_server=set_origin 368s and p1.pa_client<>sub1.sub_receiver 368s ) 368s and sub1.sub_provider=set_origin 368s --exclude any subscribers that are not 368s --direct subscribers of all sets on the 368s --origin 368s and sub1.sub_receiver not in 368s (select direct_recv.sub_receiver 368s from 368s 368s (--all direct receivers of the first set 368s select subs2.sub_receiver 368s from public.sl_subscribe subs2 368s where subs2.sub_provider=set1.set_origin 368s and subs2.sub_set=set1.set_id) as 368s direct_recv 368s inner join 368s (--all other sets from the origin 368s select set_id from public.sl_set set2 368s where set2.set_origin=set1.set_origin 368s and set2.set_id<>sub1.sub_set) 368s as othersets on(true) 368s left outer join public.sl_subscribe subs3 368s on(subs3.sub_set=othersets.set_id 368s and subs3.sub_forward=true 368s and subs3.sub_provider=set1.set_origin 368s and direct_recv.sub_receiver=subs3.sub_receiver) 368s where subs3.sub_receiver is null 368s ); 368s CREATE VIEW 368s create sequence public.sl_local_node_id 368s MINVALUE -1; 368s CREATE SEQUENCE 368s SELECT setval('public.sl_local_node_id', -1); 368s setval 368s -------- 368s -1 368s (1 row) 368s 368s comment on sequence public.sl_local_node_id is 'The local node ID is initialized to -1, meaning that this node is not initialized yet.'; 368s COMMENT 368s create sequence public.sl_event_seq; 368s CREATE SEQUENCE 368s comment on sequence public.sl_event_seq is 'The sequence for numbering events originating from this node.'; 368s COMMENT 368s select setval('public.sl_event_seq', 5000000000); 368s setval 368s ------------ 368s 5000000000 368s (1 row) 368s 368s create sequence public.sl_action_seq; 368s CREATE SEQUENCE 368s comment on sequence public.sl_action_seq is 'The sequence to number statements in the transaction logs, so that the replication engines can figure out the "agreeable" order of statements.'; 368s COMMENT 368s create sequence public.sl_log_status 368s MINVALUE 0 MAXVALUE 3; 368s CREATE SEQUENCE 368s SELECT setval('public.sl_log_status', 0); 368s setval 368s -------- 368s 0 368s (1 row) 368s 368s comment on sequence public.sl_log_status is ' 368s Bit 0x01 determines the currently active log table 368s Bit 0x02 tells if the engine needs to read both logs 368s after switching until the old log is clean and truncated. 368s 368s Possible values: 368s 0 sl_log_1 active, sl_log_2 clean 368s 1 sl_log_2 active, sl_log_1 clean 368s 2 sl_log_1 active, sl_log_2 unknown - cleanup 368s 3 sl_log_2 active, sl_log_1 unknown - cleanup 368s 368s This is not yet in use. 368s '; 368s COMMENT 368s create table public.sl_config_lock ( 368s dummy integer 368s ); 368s CREATE TABLE 368s comment on table public.sl_config_lock is 'This table exists solely to prevent overlapping execution of configuration change procedures and the resulting possible deadlocks. 368s '; 368s COMMENT 368s comment on column public.sl_config_lock.dummy is 'No data ever goes in this table so the contents never matter. Indeed, this column does not really need to exist.'; 368s COMMENT 368s create table public.sl_event_lock ( 368s dummy integer 368s ); 368s CREATE TABLE 368s comment on table public.sl_event_lock is 'This table exists solely to prevent multiple connections from concurrently creating new events and perhaps getting them out of order.'; 368s COMMENT 368s comment on column public.sl_event_lock.dummy is 'No data ever goes in this table so the contents never matter. Indeed, this column does not really need to exist.'; 368s COMMENT 368s create table public.sl_archive_counter ( 368s ac_num bigint, 368s ac_timestamp timestamptz 368s ) without oids; 368s CREATE TABLE 368s comment on table public.sl_archive_counter is 'Table used to generate the log shipping archive number. 368s '; 368s COMMENT 368s comment on column public.sl_archive_counter.ac_num is 'Counter of SYNC ID used in log shipping as the archive number'; 368s COMMENT 368s comment on column public.sl_archive_counter.ac_timestamp is 'Time at which the archive log was generated on the subscriber'; 368s COMMENT 368s insert into public.sl_archive_counter (ac_num, ac_timestamp) 368s values (0, 'epoch'::timestamptz); 368s INSERT 0 1 368s create table public.sl_components ( 368s co_actor text not null primary key, 368s co_pid integer not null, 368s co_node integer not null, 368s co_connection_pid integer not null, 368s co_activity text, 368s co_starttime timestamptz not null, 368s co_event bigint, 368s co_eventtype text 368s ) without oids; 368s CREATE TABLE 368s comment on table public.sl_components is 'Table used to monitor what various slon/slonik components are doing'; 368s COMMENT 368s comment on column public.sl_components.co_actor is 'which component am I?'; 368s COMMENT 368s comment on column public.sl_components.co_pid is 'my process/thread PID on node where slon runs'; 368s COMMENT 368s comment on column public.sl_components.co_node is 'which node am I servicing?'; 368s COMMENT 368s comment on column public.sl_components.co_connection_pid is 'PID of database connection being used on database server'; 368s COMMENT 368s comment on column public.sl_components.co_activity is 'activity that I am up to'; 368s COMMENT 368s comment on column public.sl_components.co_starttime is 'when did my activity begin? (timestamp reported as per slon process on server running slon)'; 368s COMMENT 368s comment on column public.sl_components.co_eventtype is 'what kind of event am I processing? (commonly n/a for event loop main threads)'; 368s COMMENT 368s comment on column public.sl_components.co_event is 'which event have I started processing?'; 368s COMMENT 368s CREATE OR replace function public.agg_text_sum(txt_before TEXT, txt_new TEXT) RETURNS TEXT AS 368s $BODY$ 368s DECLARE 368s c_delim text; 368s BEGIN 368s c_delim = ','; 368s IF (txt_before IS NULL or txt_before='') THEN 368s RETURN txt_new; 368s END IF; 368s RETURN txt_before || c_delim || txt_new; 368s END; 368s $BODY$ 368s LANGUAGE plpgsql; 368s CREATE FUNCTION 368s comment on function public.agg_text_sum(text,text) is 368s 'An accumulator function used by the slony string_agg function to 368s aggregate rows into a string'; 368s COMMENT 368s CREATE AGGREGATE public.string_agg(text) ( 368s SFUNC=public.agg_text_sum, 368s STYPE=text, 368s INITCOND='' 368s ); 368s CREATE AGGREGATE 368s grant usage on schema public to public; 368s GRANT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s NOTICE: checked validity of cluster main namespace - OK! 368s NOTICE: function public.clonenodeprepare(int4,int4,text) does not exist, skipping 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text, ev_data7 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text, ev_data7 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text, ev_data7 text, ev_data8 text) 368s returns bigint 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__createEvent' 368s language C 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.createEvent (p_cluster_name name, p_event_type text, ev_data1 text, ev_data2 text, ev_data3 text, ev_data4 text, ev_data5 text, ev_data6 text, ev_data7 text, ev_data8 text) is 368s 'FUNCTION createEvent (cluster_name, ev_type [, ev_data [...]]) 368s 368s Create an sl_event entry'; 368s COMMENT 368s create or replace function public.denyAccess () 368s returns trigger 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__denyAccess' 368s language C 368s security definer; 368s CREATE FUNCTION 368s comment on function public.denyAccess () is 368s 'Trigger function to prevent modifications to a table on a subscriber'; 368s COMMENT 368s grant execute on function public.denyAccess () to public; 368s GRANT 368s create or replace function public.lockedSet () 368s returns trigger 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__lockedSet' 368s language C; 368s CREATE FUNCTION 368s comment on function public.lockedSet () is 368s 'Trigger function to prevent modifications to a table before and after a moveSet()'; 368s COMMENT 368s create or replace function public.getLocalNodeId (p_cluster name) returns int4 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__getLocalNodeId' 368s language C 368s security definer; 368s CREATE FUNCTION 368s grant execute on function public.getLocalNodeId (p_cluster name) to public; 368s GRANT 368s comment on function public.getLocalNodeId (p_cluster name) is 368s 'Returns the node ID of the node being serviced on the local database'; 368s COMMENT 368s create or replace function public.getModuleVersion () returns text 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__getModuleVersion' 368s language C 368s security definer; 368s CREATE FUNCTION 368s grant execute on function public.getModuleVersion () to public; 368s GRANT 368s comment on function public.getModuleVersion () is 368s 'Returns the compiled-in version number of the Slony-I shared object'; 368s COMMENT 368s create or replace function public.resetSession() returns text 368s as '$libdir/slony1_funcs.2.2.11','_Slony_I_2_2_11__resetSession' 368s language C; 368s CREATE FUNCTION 368s create or replace function public.logApply () returns trigger 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__logApply' 368s language C 368s security definer; 368s CREATE FUNCTION 368s create or replace function public.logApplySetCacheSize (p_size int4) 368s returns int4 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__logApplySetCacheSize' 368s language C; 368s CREATE FUNCTION 368s create or replace function public.logApplySaveStats (p_cluster name, p_origin int4, p_duration interval) 368s returns int4 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__logApplySaveStats' 368s language C; 368s CREATE FUNCTION 368s create or replace function public.checkmoduleversion () returns text as $$ 368s declare 368s moduleversion text; 368s begin 368s select into moduleversion public.getModuleVersion(); 368s if moduleversion <> '2.2.11' then 368s raise exception 'Slonik version: 2.2.11 != Slony-I version in PG build %', 368s moduleversion; 368s end if; 368s return null; 368s end;$$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.checkmoduleversion () is 368s 'Inline test function that verifies that slonik request for STORE 368s NODE/INIT CLUSTER is being run against a conformant set of 368s schema/functions.'; 368s COMMENT 368s select public.checkmoduleversion(); 368s checkmoduleversion 368s -------------------- 368s 368s (1 row) 368s 368s create or replace function public.decode_tgargs(bytea) returns text[] as 368s '$libdir/slony1_funcs.2.2.11','_Slony_I_2_2_11__slon_decode_tgargs' language C security definer; 368s CREATE FUNCTION 368s comment on function public.decode_tgargs(bytea) is 368s 'Translates the contents of pg_trigger.tgargs to an array of text arguments'; 368s COMMENT 368s grant execute on function public.decode_tgargs(bytea) to public; 368s GRANT 368s create or replace function public.check_namespace_validity () returns boolean as $$ 368s declare 368s c_cluster text; 368s begin 368s c_cluster := 'main'; 368s if c_cluster !~ E'^[[:alpha:]_][[:alnum:]_\$]{0,62}$' then 368s raise exception 'Cluster name % is not a valid SQL symbol!', c_cluster; 368s else 368s raise notice 'checked validity of cluster % namespace - OK!', c_cluster; 368s end if; 368s return 't'; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s select public.check_namespace_validity(); 368s check_namespace_validity 368s -------------------------- 368s t 368s (1 row) 368s 368s drop function public.check_namespace_validity(); 368s DROP FUNCTION 368s create or replace function public.logTrigger () returns trigger 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__logTrigger' 368s language C 368s security definer; 368s CREATE FUNCTION 368s comment on function public.logTrigger () is 368s 'This is the trigger that is executed on the origin node that causes 368s updates to be recorded in sl_log_1/sl_log_2.'; 368s COMMENT 368s grant execute on function public.logTrigger () to public; 368s GRANT 368s create or replace function public.terminateNodeConnections (p_failed_node int4) returns int4 368s as $$ 368s declare 368s v_row record; 368s begin 368s for v_row in select nl_nodeid, nl_conncnt, 368s nl_backendpid from public.sl_nodelock 368s where nl_nodeid = p_failed_node for update 368s loop 368s perform public.killBackend(v_row.nl_backendpid, 'TERM'); 368s delete from public.sl_nodelock 368s where nl_nodeid = v_row.nl_nodeid 368s and nl_conncnt = v_row.nl_conncnt; 368s end loop; 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.terminateNodeConnections (p_failed_node int4) is 368s 'terminates all backends that have registered to be from the given node'; 368s COMMENT 368s create or replace function public.killBackend (p_pid int4, p_signame text) returns int4 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__killBackend' 368s language C; 368s CREATE FUNCTION 368s comment on function public.killBackend(p_pid int4, p_signame text) is 368s 'Send a signal to a postgres process. Requires superuser rights'; 368s COMMENT 368s create or replace function public.seqtrack (p_seqid int4, p_seqval int8) returns int8 368s as '$libdir/slony1_funcs.2.2.11', '_Slony_I_2_2_11__seqtrack' 368s strict language C; 368s CREATE FUNCTION 368s comment on function public.seqtrack(p_seqid int4, p_seqval int8) is 368s 'Returns NULL if seqval has not changed since the last call for seqid'; 368s COMMENT 368s create or replace function public.slon_quote_brute(p_tab_fqname text) returns text 368s as $$ 368s declare 368s v_fqname text default ''; 368s begin 368s v_fqname := '"' || replace(p_tab_fqname,'"','""') || '"'; 368s return v_fqname; 368s end; 368s $$ language plpgsql immutable; 368s CREATE FUNCTION 368s comment on function public.slon_quote_brute(p_tab_fqname text) is 368s 'Brutally quote the given text'; 368s COMMENT 368s create or replace function public.slon_quote_input(p_tab_fqname text) returns text as $$ 368s declare 368s v_nsp_name text; 368s v_tab_name text; 368s v_i integer; 368s v_l integer; 368s v_pq2 integer; 368s begin 368s v_l := length(p_tab_fqname); 368s 368s -- Let us search for the dot 368s if p_tab_fqname like '"%' then 368s -- if the first part of the ident starts with a double quote, search 368s -- for the closing double quote, skipping over double double quotes. 368s v_i := 2; 368s while v_i <= v_l loop 368s if substr(p_tab_fqname, v_i, 1) != '"' then 368s v_i := v_i + 1; 368s else 368s v_i := v_i + 1; 368s if substr(p_tab_fqname, v_i, 1) != '"' then 368s exit; 368s end if; 368s v_i := v_i + 1; 368s end if; 368s end loop; 368s else 368s -- first part of ident is not quoted, search for the dot directly 368s v_i := 1; 368s while v_i <= v_l loop 368s if substr(p_tab_fqname, v_i, 1) = '.' then 368s exit; 368s end if; 368s v_i := v_i + 1; 368s end loop; 368s end if; 368s 368s -- v_i now points at the dot or behind the string. 368s 368s if substr(p_tab_fqname, v_i, 1) = '.' then 368s -- There is a dot now, so split the ident into its namespace 368s -- and objname parts and make sure each is quoted 368s v_nsp_name := substr(p_tab_fqname, 1, v_i - 1); 368s v_tab_name := substr(p_tab_fqname, v_i + 1); 368s if v_nsp_name not like '"%' then 368s v_nsp_name := '"' || replace(v_nsp_name, '"', '""') || 368s '"'; 368s end if; 368s if v_tab_name not like '"%' then 368s v_tab_name := '"' || replace(v_tab_name, '"', '""') || 368s '"'; 368s end if; 368s 368s return v_nsp_name || '.' || v_tab_name; 368s else 368s -- No dot ... must be just an ident without schema 368s if p_tab_fqname like '"%' then 368s return p_tab_fqname; 368s else 368s return '"' || replace(p_tab_fqname, '"', '""') || '"'; 368s end if; 368s end if; 368s 368s end;$$ language plpgsql immutable; 368s CREATE FUNCTION 368s comment on function public.slon_quote_input(p_text text) is 368s 'quote all words that aren''t quoted yet'; 368s COMMENT 368s create or replace function public.slonyVersionMajor() 368s returns int4 368s as $$ 368s begin 368s return 2; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.slonyVersionMajor () is 368s 'Returns the major version number of the slony schema'; 368s COMMENT 368s create or replace function public.slonyVersionMinor() 368s returns int4 368s as $$ 368s begin 368s return 2; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.slonyVersionMinor () is 368s 'Returns the minor version number of the slony schema'; 368s COMMENT 368s create or replace function public.slonyVersionPatchlevel() 368s returns int4 368s as $$ 368s begin 368s return 11; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.slonyVersionPatchlevel () is 368s 'Returns the version patch level of the slony schema'; 368s COMMENT 368s create or replace function public.slonyVersion() 368s returns text 368s as $$ 368s begin 368s return public.slonyVersionMajor()::text || '.' || 368s public.slonyVersionMinor()::text || '.' || 368s public.slonyVersionPatchlevel()::text ; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.slonyVersion() is 368s 'Returns the version number of the slony schema'; 368s COMMENT 368s create or replace function public.registry_set_int4(p_key text, p_value int4) 368s returns int4 as $$ 368s BEGIN 368s if p_value is null then 368s delete from public.sl_registry 368s where reg_key = p_key; 368s else 368s lock table public.sl_registry; 368s update public.sl_registry 368s set reg_int4 = p_value 368s where reg_key = p_key; 368s if not found then 368s insert into public.sl_registry (reg_key, reg_int4) 368s values (p_key, p_value); 368s end if; 368s end if; 368s return p_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_set_int4(p_key text, p_value int4) is 368s 'registry_set_int4(key, value) 368s 368s Set or delete a registry value'; 368s COMMENT 368s create or replace function public.registry_get_int4(p_key text, p_default int4) 368s returns int4 as $$ 368s DECLARE 368s v_value int4; 368s BEGIN 368s select reg_int4 into v_value from public.sl_registry 368s where reg_key = p_key; 368s if not found then 368s v_value = p_default; 368s if p_default notnull then 368s perform public.registry_set_int4(p_key, p_default); 368s end if; 368s else 368s if v_value is null then 368s raise exception 'Slony-I: registry key % is not an int4 value', 368s p_key; 368s end if; 368s end if; 368s return v_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_get_int4(p_key text, p_default int4) is 368s 'registry_get_int4(key, value) 368s 368s Get a registry value. If not present, set and return the default.'; 368s COMMENT 368s create or replace function public.registry_set_text(p_key text, p_value text) 368s returns text as $$ 368s BEGIN 368s if p_value is null then 368s delete from public.sl_registry 368s where reg_key = p_key; 368s else 368s lock table public.sl_registry; 368s update public.sl_registry 368s set reg_text = p_value 368s where reg_key = p_key; 368s if not found then 368s insert into public.sl_registry (reg_key, reg_text) 368s values (p_key, p_value); 368s end if; 368s end if; 368s return p_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_set_text(text, text) is 368s 'registry_set_text(key, value) 368s 368s Set or delete a registry value'; 368s COMMENT 368s create or replace function public.registry_get_text(p_key text, p_default text) 368s returns text as $$ 368s DECLARE 368s v_value text; 368s BEGIN 368s select reg_text into v_value from public.sl_registry 368s where reg_key = p_key; 368s if not found then 368s v_value = p_default; 368s if p_default notnull then 368s perform public.registry_set_text(p_key, p_default); 368s end if; 368s else 368s if v_value is null then 368s raise exception 'Slony-I: registry key % is not a text value', 368s p_key; 368s end if; 368s end if; 368s return v_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_get_text(p_key text, p_default text) is 368s 'registry_get_text(key, value) 368s 368s Get a registry value. If not present, set and return the default.'; 368s COMMENT 368s create or replace function public.registry_set_timestamp(p_key text, p_value timestamptz) 368s returns timestamp as $$ 368s BEGIN 368s if p_value is null then 368s delete from public.sl_registry 368s where reg_key = p_key; 368s else 368s lock table public.sl_registry; 368s update public.sl_registry 368s set reg_timestamp = p_value 368s where reg_key = p_key; 368s if not found then 368s insert into public.sl_registry (reg_key, reg_timestamp) 368s values (p_key, p_value); 368s end if; 368s end if; 368s return p_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_set_timestamp(p_key text, p_value timestamptz) is 368s 'registry_set_timestamp(key, value) 368s 368s Set or delete a registry value'; 368s COMMENT 368s create or replace function public.registry_get_timestamp(p_key text, p_default timestamptz) 368s returns timestamp as $$ 368s DECLARE 368s v_value timestamp; 368s BEGIN 368s select reg_timestamp into v_value from public.sl_registry 368s where reg_key = p_key; 368s if not found then 368s v_value = p_default; 368s if p_default notnull then 368s perform public.registry_set_timestamp(p_key, p_default); 368s end if; 368s else 368s if v_value is null then 368s raise exception 'Slony-I: registry key % is not an timestamp value', 368s p_key; 368s end if; 368s end if; 368s return v_value; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registry_get_timestamp(p_key text, p_default timestamptz) is 368s 'registry_get_timestamp(key, value) 368s 368s Get a registry value. If not present, set and return the default.'; 368s COMMENT 368s create or replace function public.cleanupNodelock () 368s returns int4 368s as $$ 368s declare 368s v_row record; 368s begin 368s for v_row in select nl_nodeid, nl_conncnt, nl_backendpid 368s from public.sl_nodelock 368s for update 368s loop 368s if public.killBackend(v_row.nl_backendpid, 'NULL') < 0 then 368s raise notice 'Slony-I: cleanup stale sl_nodelock entry for pid=%', 368s v_row.nl_backendpid; 368s delete from public.sl_nodelock where 368s nl_nodeid = v_row.nl_nodeid and 368s nl_conncnt = v_row.nl_conncnt; 368s end if; 368s end loop; 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.cleanupNodelock() is 368s 'Clean up stale entries when restarting slon'; 368s COMMENT 368s create or replace function public.registerNodeConnection (p_nodeid int4) 368s returns int4 368s as $$ 368s begin 368s insert into public.sl_nodelock 368s (nl_nodeid, nl_backendpid) 368s values 368s (p_nodeid, pg_backend_pid()); 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.registerNodeConnection (p_nodeid int4) is 368s 'Register (uniquely) the node connection so that only one slon can service the node'; 368s COMMENT 368s create or replace function public.initializeLocalNode (p_local_node_id int4, p_comment text) 368s returns int4 368s as $$ 368s declare 368s v_old_node_id int4; 368s v_first_log_no int4; 368s v_event_seq int8; 368s begin 368s -- ---- 368s -- Make sure this node is uninitialized or got reset 368s -- ---- 368s select last_value::int4 into v_old_node_id from public.sl_local_node_id; 368s if v_old_node_id != -1 then 368s raise exception 'Slony-I: This node is already initialized'; 368s end if; 368s 368s -- ---- 368s -- Set sl_local_node_id to the requested value and add our 368s -- own system to sl_node. 368s -- ---- 368s perform setval('public.sl_local_node_id', p_local_node_id); 368s perform public.storeNode_int (p_local_node_id, p_comment); 368s 368s if (pg_catalog.current_setting('max_identifier_length')::integer - pg_catalog.length('public')) < 5 then 368s raise notice 'Slony-I: Cluster name length [%] versus system max_identifier_length [%] ', pg_catalog.length('public'), pg_catalog.current_setting('max_identifier_length'); 368s raise notice 'leaves narrow/no room for some Slony-I-generated objects (such as indexes).'; 368s raise notice 'You may run into problems later!'; 368s end if; 368s 368s -- 368s -- Put the apply trigger onto sl_log_1 and sl_log_2 368s -- 368s create trigger apply_trigger 368s before INSERT on public.sl_log_1 368s for each row execute procedure public.logApply('_main'); 368s alter table public.sl_log_1 368s enable replica trigger apply_trigger; 368s create trigger apply_trigger 368s before INSERT on public.sl_log_2 368s for each row execute procedure public.logApply('_main'); 368s alter table public.sl_log_2 368s enable replica trigger apply_trigger; 368s 368s return p_local_node_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.initializeLocalNode (p_local_node_id int4, p_comment text) is 368s 'no_id - Node ID # 368s no_comment - Human-oriented comment 368s 368s Initializes the new node, no_id'; 368s COMMENT 368s create or replace function public.storeNode (p_no_id int4, p_no_comment text) 368s returns bigint 368s as $$ 368s begin 368s perform public.storeNode_int (p_no_id, p_no_comment); 368s return public.createEvent('_main', 'STORE_NODE', 368s p_no_id::text, p_no_comment::text); 368s end; 368s $$ language plpgsql 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.storeNode(p_no_id int4, p_no_comment text) is 368s 'no_id - Node ID # 368s no_comment - Human-oriented comment 368s 368s Generate the STORE_NODE event for node no_id'; 368s COMMENT 368s create or replace function public.storeNode_int (p_no_id int4, p_no_comment text) 368s returns int4 368s as $$ 368s declare 368s v_old_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check if the node exists 368s -- ---- 368s select * into v_old_row 368s from public.sl_node 368s where no_id = p_no_id 368s for update; 368s if found then 368s -- ---- 368s -- Node exists, update the existing row. 368s -- ---- 368s update public.sl_node 368s set no_comment = p_no_comment 368s where no_id = p_no_id; 368s else 368s -- ---- 368s -- New node, insert the sl_node row 368s -- ---- 368s insert into public.sl_node 368s (no_id, no_active, no_comment,no_failed) values 368s (p_no_id, 'f', p_no_comment,false); 368s end if; 368s 368s return p_no_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storeNode_int(p_no_id int4, p_no_comment text) is 368s 'no_id - Node ID # 368s no_comment - Human-oriented comment 368s 368s Internal function to process the STORE_NODE event for node no_id'; 368s COMMENT 368s create or replace function public.enableNode (p_no_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_node_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that we are the node to activate and that we are 368s -- currently disabled. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select * into v_node_row 368s from public.sl_node 368s where no_id = p_no_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: node % not found', p_no_id; 368s end if; 368s if v_node_row.no_active then 368s raise exception 'Slony-I: node % is already active', p_no_id; 368s end if; 368s 368s -- ---- 368s -- Activate this node and generate the ENABLE_NODE event 368s -- ---- 368s perform public.enableNode_int (p_no_id); 368s return public.createEvent('_main', 'ENABLE_NODE', 368s p_no_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.enableNode(p_no_id int4) is 368s 'no_id - Node ID # 368s 368s Generate the ENABLE_NODE event for node no_id'; 368s COMMENT 368s create or replace function public.enableNode_int (p_no_id int4) 368s returns int4 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_node_row record; 368s v_sub_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that the node is inactive 368s -- ---- 368s select * into v_node_row 368s from public.sl_node 368s where no_id = p_no_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: node % not found', p_no_id; 368s end if; 368s if v_node_row.no_active then 368s return p_no_id; 368s end if; 368s 368s -- ---- 368s -- Activate the node and generate sl_confirm status rows for it. 368s -- ---- 368s update public.sl_node 368s set no_active = 't' 368s where no_id = p_no_id; 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno) 368s select no_id, p_no_id, 0 from public.sl_node 368s where no_id != p_no_id 368s and no_active; 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno) 368s select p_no_id, no_id, 0 from public.sl_node 368s where no_id != p_no_id 368s and no_active; 368s 368s -- ---- 368s -- Generate ENABLE_SUBSCRIPTION events for all sets that 368s -- origin here and are subscribed by the just enabled node. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s for v_sub_row in select SUB.sub_set, SUB.sub_provider from 368s public.sl_set S, 368s public.sl_subscribe SUB 368s where S.set_origin = v_local_node_id 368s and S.set_id = SUB.sub_set 368s and SUB.sub_receiver = p_no_id 368s for update of S 368s loop 368s perform public.enableSubscription (v_sub_row.sub_set, 368s v_sub_row.sub_provider, p_no_id); 368s end loop; 368s 368s return p_no_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.enableNode_int(p_no_id int4) is 368s 'no_id - Node ID # 368s 368s Internal function to process the ENABLE_NODE event for node no_id'; 368s COMMENT 368s create or replace function public.disableNode (p_no_id int4) 368s returns bigint 368s as $$ 368s begin 368s -- **** TODO **** 368s raise exception 'Slony-I: disableNode() not implemented'; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.disableNode(p_no_id int4) is 368s 'generate DISABLE_NODE event for node no_id'; 368s COMMENT 368s create or replace function public.disableNode_int (p_no_id int4) 368s returns int4 368s as $$ 368s begin 368s -- **** TODO **** 368s raise exception 'Slony-I: disableNode_int() not implemented'; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.disableNode(p_no_id int4) is 368s 'process DISABLE_NODE event for node no_id 368s 368s NOTE: This is not yet implemented!'; 368s COMMENT 368s create or replace function public.dropNode (p_no_ids int4[]) 368s returns bigint 368s as $$ 368s declare 368s v_node_row record; 368s v_idx integer; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that this got called on a different node 368s -- ---- 368s if public.getLocalNodeId('_main') = ANY (p_no_ids) then 368s raise exception 'Slony-I: DROP_NODE cannot initiate on the dropped node'; 368s end if; 368s 368s -- 368s -- if any of the deleted nodes are receivers we drop the sl_subscribe line 368s -- 368s delete from public.sl_subscribe where sub_receiver = ANY (p_no_ids); 368s 368s v_idx:=1; 368s LOOP 368s EXIT WHEN v_idx>array_upper(p_no_ids,1) ; 368s select * into v_node_row from public.sl_node 368s where no_id = p_no_ids[v_idx] 368s for update; 368s if not found then 368s raise exception 'Slony-I: unknown node ID % %', p_no_ids[v_idx],v_idx; 368s end if; 368s -- ---- 368s -- Make sure we do not break other nodes subscriptions with this 368s -- ---- 368s if exists (select true from public.sl_subscribe 368s where sub_provider = p_no_ids[v_idx]) 368s then 368s raise exception 'Slony-I: Node % is still configured as a data provider', 368s p_no_ids[v_idx]; 368s end if; 368s 368s -- ---- 368s -- Make sure no set originates there any more 368s -- ---- 368s if exists (select true from public.sl_set 368s where set_origin = p_no_ids[v_idx]) 368s then 368s raise exception 'Slony-I: Node % is still origin of one or more sets', 368s p_no_ids[v_idx]; 368s end if; 368s 368s -- ---- 368s -- Call the internal drop functionality and generate the event 368s -- ---- 368s perform public.dropNode_int(p_no_ids[v_idx]); 368s v_idx:=v_idx+1; 368s END LOOP; 368s return public.createEvent('_main', 'DROP_NODE', 368s array_to_string(p_no_ids,',')); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropNode(p_no_ids int4[]) is 368s 'generate DROP_NODE event to drop node node_id from replication'; 368s COMMENT 368s create or replace function public.dropNode_int (p_no_id int4) 368s returns int4 368s as $$ 368s declare 368s v_tab_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- If the dropped node is a remote node, clean the configuration 368s -- from all traces for it. 368s -- ---- 368s if p_no_id <> public.getLocalNodeId('_main') then 368s delete from public.sl_subscribe 368s where sub_receiver = p_no_id; 368s delete from public.sl_listen 368s where li_origin = p_no_id 368s or li_provider = p_no_id 368s or li_receiver = p_no_id; 368s delete from public.sl_path 368s where pa_server = p_no_id 368s or pa_client = p_no_id; 368s delete from public.sl_confirm 368s where con_origin = p_no_id 368s or con_received = p_no_id; 368s delete from public.sl_event 368s where ev_origin = p_no_id; 368s delete from public.sl_node 368s where no_id = p_no_id; 368s 368s return p_no_id; 368s end if; 368s 368s -- ---- 368s -- This is us ... deactivate the node for now, the daemon 368s -- will call uninstallNode() in a separate transaction. 368s -- ---- 368s update public.sl_node 368s set no_active = false 368s where no_id = p_no_id; 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return p_no_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropNode_int(p_no_id int4) is 368s 'internal function to process DROP_NODE event to drop node node_id from replication'; 368s COMMENT 368s create or replace function public.preFailover(p_failed_node int4,p_is_candidate boolean) 368s returns int4 368s as $$ 368s declare 368s v_row record; 368s v_row2 record; 368s v_n int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- All consistency checks first 368s 368s if p_is_candidate then 368s -- ---- 368s -- Check all sets originating on the failed node 368s -- ---- 368s for v_row in select set_id 368s from public.sl_set 368s where set_origin = p_failed_node 368s loop 368s -- ---- 368s -- Check that the backup node is subscribed to all sets 368s -- that originate on the failed node 368s -- ---- 368s select into v_row2 sub_forward, sub_active 368s from public.sl_subscribe 368s where sub_set = v_row.set_id 368s and sub_receiver = public.getLocalNodeId('_main'); 368s if not found then 368s raise exception 'Slony-I: cannot failover - node % is not subscribed to set %', 368s public.getLocalNodeId('_main'), v_row.set_id; 368s end if; 368s 368s -- ---- 368s -- Check that the subscription is active 368s -- ---- 368s if not v_row2.sub_active then 368s raise exception 'Slony-I: cannot failover - subscription for set % is not active', 368s v_row.set_id; 368s end if; 368s 368s -- ---- 368s -- If there are other subscribers, the backup node needs to 368s -- be a forwarder too. 368s -- ---- 368s select into v_n count(*) 368s from public.sl_subscribe 368s where sub_set = v_row.set_id 368s and sub_receiver <> public.getLocalNodeId('_main'); 368s if v_n > 0 and not v_row2.sub_forward then 368s raise exception 'Slony-I: cannot failover - node % is not a forwarder of set %', 368s public.getLocalNodeId('_main'), v_row.set_id; 368s end if; 368s end loop; 368s end if; 368s 368s -- ---- 368s -- Terminate all connections of the failed node the hard way 368s -- ---- 368s perform public.terminateNodeConnections(p_failed_node); 368s 368s update public.sl_path set pa_conninfo='' WHERE 368s pa_server=p_failed_node; 368s notify "_main_Restart"; 368s -- ---- 368s -- That is it - so far. 368s -- ---- 368s return p_failed_node; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.preFailover(p_failed_node int4,is_failover_candidate boolean) is 368s 'Prepare for a failover. This function is called on all candidate nodes. 368s It blanks the paths to the failed node 368s and then restart of all node daemons.'; 368s COMMENT 368s create or replace function public.failedNode(p_failed_node int4, p_backup_node int4,p_failed_nodes integer[]) 368s returns int4 368s as $$ 368s declare 368s v_row record; 368s v_row2 record; 368s v_failed boolean; 368s v_restart_required boolean; 368s begin 368s 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s v_restart_required:=false; 368s -- 368s -- any nodes other than the backup receiving 368s -- ANY subscription from a failed node 368s -- will now get that data from the backup node. 368s update public.sl_subscribe set 368s sub_provider=p_backup_node 368s where sub_provider=p_failed_node 368s and sub_receiver<>p_backup_node 368s and sub_receiver <> ALL (p_failed_nodes); 368s if found then 368s v_restart_required:=true; 368s end if; 368s -- 368s -- if this node is receiving a subscription from the backup node 368s -- with a failed node as the provider we need to fix this. 368s update public.sl_subscribe set 368s sub_provider=p_backup_node 368s from public.sl_set 368s where set_id = sub_set 368s and set_origin=p_failed_node 368s and sub_provider = ANY(p_failed_nodes) 368s and sub_receiver=public.getLocalNodeId('_main'); 368s 368s -- ---- 368s -- Terminate all connections of the failed node the hard way 368s -- ---- 368s perform public.terminateNodeConnections(p_failed_node); 368s 368s -- Clear out the paths for the failed node. 368s -- This ensures that *this* node won't be pulling data from 368s -- the failed node even if it *does* become accessible 368s 368s update public.sl_path set pa_conninfo='' WHERE 368s pa_server=p_failed_node 368s and pa_conninfo<>''; 368s 368s if found then 368s v_restart_required:=true; 368s end if; 368s 368s v_failed := exists (select 1 from public.sl_node 368s where no_failed=true and no_id=p_failed_node); 368s 368s if not v_failed then 368s 368s update public.sl_node set no_failed=true where no_id = ANY (p_failed_nodes) 368s and no_failed=false; 368s if found then 368s v_restart_required:=true; 368s end if; 368s end if; 368s 368s if v_restart_required then 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s -- ---- 368s -- Make sure the node daemon will restart 368s -- ---- 368s notify "_main_Restart"; 368s end if; 368s 368s 368s -- ---- 368s -- That is it - so far. 368s -- ---- 368s return p_failed_node; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.failedNode(p_failed_node int4, p_backup_node int4,p_failed_nodes integer[]) is 368s 'Initiate failover from failed_node to backup_node. This function must be called on all nodes, 368s and then waited for the restart of all node daemons.'; 368s COMMENT 368s create or replace function public.failedNode2 (p_failed_node int4, p_backup_node int4, p_ev_seqno int8, p_failed_nodes integer[]) 368s returns bigint 368s as $$ 368s declare 368s v_row record; 368s v_new_event bigint; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s select * into v_row 368s from public.sl_event 368s where ev_origin = p_failed_node 368s and ev_seqno = p_ev_seqno; 368s if not found then 368s raise exception 'Slony-I: event %,% not found', 368s p_failed_node, p_ev_seqno; 368s end if; 368s 368s update public.sl_node set no_failed=true where no_id = ANY 368s (p_failed_nodes) and no_failed=false; 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s -- ---- 368s -- Make sure the node daemon will restart 368s -- ---- 368s raise notice 'calling restart node %',p_failed_node; 368s 368s notify "_main_Restart"; 368s 368s select public.createEvent('_main','FAILOVER_NODE', 368s p_failed_node::text,p_ev_seqno::text, 368s array_to_string(p_failed_nodes,',')) 368s into v_new_event; 368s 368s 368s return v_new_event; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.failedNode2 (p_failed_node int4, p_backup_node int4, p_ev_seqno int8,p_failed_nodes integer[] ) is 368s 'FUNCTION failedNode2 (failed_node, backup_node, set_id, ev_seqno, ev_seqfake,p_failed_nodes) 368s 368s On the node that has the highest sequence number of the failed node, 368s fake the FAILOVER_SET event.'; 368s COMMENT 368s create or replace function public.failedNode3 (p_failed_node int4, p_backup_node int4,p_seq_no bigint) 368s returns int4 368s as $$ 368s declare 368s 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s perform public.failoverSet_int(p_failed_node, 368s p_backup_node,p_seq_no); 368s 368s notify "_main_Restart"; 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s create or replace function public.failoverSet_int (p_failed_node int4, p_backup_node int4,p_last_seqno bigint) 368s returns int4 368s as $$ 368s declare 368s v_row record; 368s v_last_sync int8; 368s v_set int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s SELECT max(ev_seqno) into v_last_sync FROM public.sl_event where 368s ev_origin=p_failed_node; 368s if v_last_sync > p_last_seqno then 368s -- this node is ahead of the last sequence number from the 368s -- failed node that the backup node has. 368s -- this node must unsubscribe from all sets from the origin. 368s for v_set in select set_id from public.sl_set where 368s set_origin=p_failed_node 368s loop 368s raise warning 'Slony is dropping the subscription of set % found sync %s bigger than %s ' 368s , v_set, v_last_sync::text, p_last_seqno::text; 368s perform public.unsubscribeSet(v_set, 368s public.getLocalNodeId('_main'), 368s true); 368s end loop; 368s delete from public.sl_event where ev_origin=p_failed_node 368s and ev_seqno > p_last_seqno; 368s end if; 368s -- ---- 368s -- Change the origin of the set now to the backup node. 368s -- On the backup node this includes changing all the 368s -- trigger and protection stuff 368s for v_set in select set_id from public.sl_set where 368s set_origin=p_failed_node 368s loop 368s -- ---- 368s if p_backup_node = public.getLocalNodeId('_main') then 368s delete from public.sl_setsync 368s where ssy_setid = v_set; 368s delete from public.sl_subscribe 368s where sub_set = v_set 368s and sub_receiver = p_backup_node; 368s update public.sl_set 368s set set_origin = p_backup_node 368s where set_id = v_set; 368s update public.sl_subscribe 368s set sub_provider=p_backup_node 368s FROM public.sl_node receive_node 368s where sub_set = v_set 368s and sub_provider=p_failed_node 368s and sub_receiver=receive_node.no_id 368s and receive_node.no_failed=false; 368s 368s for v_row in select * from public.sl_table 368s where tab_set = v_set 368s order by tab_id 368s loop 368s perform public.alterTableConfigureTriggers(v_row.tab_id); 368s end loop; 368s else 368s raise notice 'deleting from sl_subscribe all rows with receiver %', 368s p_backup_node; 368s 368s delete from public.sl_subscribe 368s where sub_set = v_set 368s and sub_receiver = p_backup_node; 368s 368s update public.sl_subscribe 368s set sub_provider=p_backup_node 368s FROM public.sl_node receive_node 368s where sub_set = v_set 368s and sub_provider=p_failed_node 368s and sub_provider=p_failed_node 368s and sub_receiver=receive_node.no_id 368s and receive_node.no_failed=false; 368s update public.sl_set 368s set set_origin = p_backup_node 368s where set_id = v_set; 368s -- ---- 368s -- If we are a subscriber of the set ourself, change our 368s -- setsync status to reflect the new set origin. 368s -- ---- 368s if exists (select true from public.sl_subscribe 368s where sub_set = v_set 368s and sub_receiver = public.getLocalNodeId( 368s '_main')) 368s then 368s delete from public.sl_setsync 368s where ssy_setid = v_set; 368s 368s select coalesce(max(ev_seqno), 0) into v_last_sync 368s from public.sl_event 368s where ev_origin = p_backup_node 368s and ev_type = 'SYNC'; 368s if v_last_sync > 0 then 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s select v_set, p_backup_node, v_last_sync, 368s ev_snapshot, NULL 368s from public.sl_event 368s where ev_origin = p_backup_node 368s and ev_seqno = v_last_sync; 368s else 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s values (v_set, p_backup_node, '0', 368s '1:1:', NULL); 368s end if; 368s end if; 368s end if; 368s end loop; 368s 368s --If there are any subscriptions with 368s --the failed_node being the provider then 368s --we want to redirect those subscriptions 368s --to come from the backup node. 368s -- 368s -- The backup node should be a valid 368s -- provider for all subscriptions served 368s -- by the failed node. (otherwise it 368s -- wouldn't be a allowable backup node). 368s -- delete from public.sl_subscribe 368s -- where sub_receiver=p_backup_node; 368s 368s update public.sl_subscribe 368s set sub_provider=p_backup_node 368s from public.sl_node 368s where sub_provider=p_failed_node 368s and sl_node.no_id=sub_receiver 368s and sl_node.no_failed=false 368s and sub_receiver<>p_backup_node; 368s 368s update public.sl_subscribe 368s set sub_provider=(select set_origin from 368s public.sl_set where set_id= 368s sub_set) 368s where sub_provider=p_failed_node 368s and sub_receiver=p_backup_node; 368s 368s update public.sl_node 368s set no_active=false WHERE 368s no_id=p_failed_node; 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s 368s return p_failed_node; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.failoverSet_int (p_failed_node int4, p_backup_node int4,p_seqno bigint) is 368s 'FUNCTION failoverSet_int (failed_node, backup_node, set_id, wait_seqno) 368s 368s Finish failover for one set.'; 368s COMMENT 368s create or replace function public.uninstallNode () 368s returns int4 368s as $$ 368s declare 368s v_tab_row record; 368s begin 368s raise notice 'Slony-I: Please drop schema "_main"'; 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.uninstallNode() is 368s 'Reset the whole database to standalone by removing the whole 368s replication system.'; 368s COMMENT 368s DROP FUNCTION IF EXISTS public.cloneNodePrepare(int4,int4,text); 368s DROP FUNCTION 368s create or replace function public.cloneNodePrepare (p_no_id int4, p_no_provider int4, p_no_comment text) 368s returns bigint 368s as $$ 368s begin 368s perform public.cloneNodePrepare_int (p_no_id, p_no_provider, p_no_comment); 368s return public.createEvent('_main', 'CLONE_NODE', 368s p_no_id::text, p_no_provider::text, 368s p_no_comment::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.cloneNodePrepare(p_no_id int4, p_no_provider int4, p_no_comment text) is 368s 'Prepare for cloning a node.'; 368s COMMENT 368s create or replace function public.cloneNodePrepare_int (p_no_id int4, p_no_provider int4, p_no_comment text) 368s returns int4 368s as $$ 368s declare 368s v_dummy int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s update public.sl_node set 368s no_active = np.no_active, 368s no_comment = np.no_comment, 368s no_failed = np.no_failed 368s from public.sl_node np 368s where np.no_id = p_no_provider 368s and sl_node.no_id = p_no_id; 368s if not found then 368s insert into public.sl_node 368s (no_id, no_active, no_comment,no_failed) 368s select p_no_id, no_active, p_no_comment, no_failed 368s from public.sl_node 368s where no_id = p_no_provider; 368s end if; 368s 368s insert into public.sl_path 368s (pa_server, pa_client, pa_conninfo, pa_connretry) 368s select pa_server, p_no_id, '', pa_connretry 368s from public.sl_path 368s where pa_client = p_no_provider 368s and (pa_server, p_no_id) not in (select pa_server, pa_client 368s from public.sl_path); 368s 368s insert into public.sl_path 368s (pa_server, pa_client, pa_conninfo, pa_connretry) 368s select p_no_id, pa_client, '', pa_connretry 368s from public.sl_path 368s where pa_server = p_no_provider 368s and (p_no_id, pa_client) not in (select pa_server, pa_client 368s from public.sl_path); 368s 368s insert into public.sl_subscribe 368s (sub_set, sub_provider, sub_receiver, sub_forward, sub_active) 368s select sub_set, sub_provider, p_no_id, sub_forward, sub_active 368s from public.sl_subscribe 368s where sub_receiver = p_no_provider; 368s 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno, con_timestamp) 368s select con_origin, p_no_id, con_seqno, con_timestamp 368s from public.sl_confirm 368s where con_received = p_no_provider; 368s 368s perform public.RebuildListenEntries(); 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.cloneNodePrepare_int(p_no_id int4, p_no_provider int4, p_no_comment text) is 368s 'Internal part of cloneNodePrepare().'; 368s COMMENT 368s create or replace function public.cloneNodeFinish (p_no_id int4, p_no_provider int4) 368s returns int4 368s as $$ 368s declare 368s v_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s perform "pg_catalog".setval('public.sl_local_node_id', p_no_id); 368s perform public.resetSession(); 368s for v_row in select sub_set from public.sl_subscribe 368s where sub_receiver = p_no_id 368s loop 368s perform public.updateReloid(v_row.sub_set, p_no_id); 368s end loop; 368s 368s perform public.RebuildListenEntries(); 368s 368s delete from public.sl_confirm 368s where con_received = p_no_id; 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno, con_timestamp) 368s select con_origin, p_no_id, con_seqno, con_timestamp 368s from public.sl_confirm 368s where con_received = p_no_provider; 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno, con_timestamp) 368s select p_no_provider, p_no_id, 368s (select max(ev_seqno) from public.sl_event 368s where ev_origin = p_no_provider), current_timestamp; 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.cloneNodeFinish(p_no_id int4, p_no_provider int4) is 368s 'Internal part of cloneNodePrepare().'; 368s COMMENT 368s create or replace function public.storePath (p_pa_server int4, p_pa_client int4, p_pa_conninfo text, p_pa_connretry int4) 368s returns bigint 368s as $$ 368s begin 368s perform public.storePath_int(p_pa_server, p_pa_client, 368s p_pa_conninfo, p_pa_connretry); 368s return public.createEvent('_main', 'STORE_PATH', 368s p_pa_server::text, p_pa_client::text, 368s p_pa_conninfo::text, p_pa_connretry::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storePath (p_pa_server int4, p_pa_client int4, p_pa_conninfo text, p_pa_connretry int4) is 368s 'FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry) 368s 368s Generate the STORE_PATH event indicating that node pa_client can 368s access node pa_server using DSN pa_conninfo'; 368s COMMENT 368s create or replace function public.storePath_int (p_pa_server int4, p_pa_client int4, p_pa_conninfo text, p_pa_connretry int4) 368s returns int4 368s as $$ 368s declare 368s v_dummy int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check if the path already exists 368s -- ---- 368s select 1 into v_dummy 368s from public.sl_path 368s where pa_server = p_pa_server 368s and pa_client = p_pa_client 368s for update; 368s if found then 368s -- ---- 368s -- Path exists, update pa_conninfo 368s -- ---- 368s update public.sl_path 368s set pa_conninfo = p_pa_conninfo, 368s pa_connretry = p_pa_connretry 368s where pa_server = p_pa_server 368s and pa_client = p_pa_client; 368s else 368s -- ---- 368s -- New path 368s -- 368s -- In case we receive STORE_PATH events before we know 368s -- about the nodes involved in this, we generate those nodes 368s -- as pending. 368s -- ---- 368s if not exists (select 1 from public.sl_node 368s where no_id = p_pa_server) then 368s perform public.storeNode_int (p_pa_server, ''); 368s end if; 368s if not exists (select 1 from public.sl_node 368s where no_id = p_pa_client) then 368s perform public.storeNode_int (p_pa_client, ''); 368s end if; 368s insert into public.sl_path 368s (pa_server, pa_client, pa_conninfo, pa_connretry) values 368s (p_pa_server, p_pa_client, p_pa_conninfo, p_pa_connretry); 368s end if; 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storePath_int (p_pa_server int4, p_pa_client int4, p_pa_conninfo text, p_pa_connretry int4) is 368s 'FUNCTION storePath (pa_server, pa_client, pa_conninfo, pa_connretry) 368s 368s Process the STORE_PATH event indicating that node pa_client can 368s access node pa_server using DSN pa_conninfo'; 368s COMMENT 368s create or replace function public.dropPath (p_pa_server int4, p_pa_client int4) 368s returns bigint 368s as $$ 368s declare 368s v_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- There should be no existing subscriptions. Auto unsubscribing 368s -- is considered too dangerous. 368s -- ---- 368s for v_row in select sub_set, sub_provider, sub_receiver 368s from public.sl_subscribe 368s where sub_provider = p_pa_server 368s and sub_receiver = p_pa_client 368s loop 368s raise exception 368s 'Slony-I: Path cannot be dropped, subscription of set % needs it', 368s v_row.sub_set; 368s end loop; 368s 368s -- ---- 368s -- Drop all sl_listen entries that depend on this path 368s -- ---- 368s for v_row in select li_origin, li_provider, li_receiver 368s from public.sl_listen 368s where li_provider = p_pa_server 368s and li_receiver = p_pa_client 368s loop 368s perform public.dropListen( 368s v_row.li_origin, v_row.li_provider, v_row.li_receiver); 368s end loop; 368s 368s -- ---- 368s -- Now drop the path and create the event 368s -- ---- 368s perform public.dropPath_int(p_pa_server, p_pa_client); 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return public.createEvent ('_main', 'DROP_PATH', 368s p_pa_server::text, p_pa_client::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropPath (p_pa_server int4, p_pa_client int4) is 368s 'Generate DROP_PATH event to drop path from pa_server to pa_client'; 368s COMMENT 368s create or replace function public.dropPath_int (p_pa_server int4, p_pa_client int4) 368s returns int4 368s as $$ 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Remove any dangling sl_listen entries with the server 368s -- as provider and the client as receiver. This must have 368s -- been cleared out before, but obviously was not. 368s -- ---- 368s delete from public.sl_listen 368s where li_provider = p_pa_server 368s and li_receiver = p_pa_client; 368s 368s delete from public.sl_path 368s where pa_server = p_pa_server 368s and pa_client = p_pa_client; 368s 368s if found then 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return 1; 368s else 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return 0; 368s end if; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropPath_int (p_pa_server int4, p_pa_client int4) is 368s 'Process DROP_PATH event to drop path from pa_server to pa_client'; 368s COMMENT 368s create or replace function public.storeListen (p_origin int4, p_provider int4, p_receiver int4) 368s returns bigint 368s as $$ 368s begin 368s perform public.storeListen_int (p_origin, p_provider, p_receiver); 368s return public.createEvent ('_main', 'STORE_LISTEN', 368s p_origin::text, p_provider::text, p_receiver::text); 368s end; 368s $$ language plpgsql 368s called on null input; 368s CREATE FUNCTION 368s comment on function public.storeListen(p_origin int4, p_provider int4, p_receiver int4) is 368s 'FUNCTION storeListen (li_origin, li_provider, li_receiver) 368s 368s generate STORE_LISTEN event, indicating that receiver node li_receiver 368s listens to node li_provider in order to get messages coming from node 368s li_origin.'; 368s COMMENT 368s create or replace function public.storeListen_int (p_li_origin int4, p_li_provider int4, p_li_receiver int4) 368s returns int4 368s as $$ 368s declare 368s v_exists int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s select 1 into v_exists 368s from public.sl_listen 368s where li_origin = p_li_origin 368s and li_provider = p_li_provider 368s and li_receiver = p_li_receiver; 368s if not found then 368s -- ---- 368s -- In case we receive STORE_LISTEN events before we know 368s -- about the nodes involved in this, we generate those nodes 368s -- as pending. 368s -- ---- 368s if not exists (select 1 from public.sl_node 368s where no_id = p_li_origin) then 368s perform public.storeNode_int (p_li_origin, ''); 368s end if; 368s if not exists (select 1 from public.sl_node 368s where no_id = p_li_provider) then 368s perform public.storeNode_int (p_li_provider, ''); 368s end if; 368s if not exists (select 1 from public.sl_node 368s where no_id = p_li_receiver) then 368s perform public.storeNode_int (p_li_receiver, ''); 368s end if; 368s 368s insert into public.sl_listen 368s (li_origin, li_provider, li_receiver) values 368s (p_li_origin, p_li_provider, p_li_receiver); 368s end if; 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storeListen_int(p_li_origin int4, p_li_provider int4, p_li_receiver int4) is 368s 'FUNCTION storeListen_int (li_origin, li_provider, li_receiver) 368s 368s Process STORE_LISTEN event, indicating that receiver node li_receiver 368s listens to node li_provider in order to get messages coming from node 368s li_origin.'; 368s COMMENT 368s create or replace function public.dropListen (p_li_origin int4, p_li_provider int4, p_li_receiver int4) 368s returns bigint 368s as $$ 368s begin 368s perform public.dropListen_int(p_li_origin, 368s p_li_provider, p_li_receiver); 368s 368s return public.createEvent ('_main', 'DROP_LISTEN', 368s p_li_origin::text, p_li_provider::text, p_li_receiver::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropListen(p_li_origin int4, p_li_provider int4, p_li_receiver int4) is 368s 'dropListen (li_origin, li_provider, li_receiver) 368s 368s Generate the DROP_LISTEN event.'; 368s COMMENT 368s create or replace function public.dropListen_int (p_li_origin int4, p_li_provider int4, p_li_receiver int4) 368s returns int4 368s as $$ 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s delete from public.sl_listen 368s where li_origin = p_li_origin 368s and li_provider = p_li_provider 368s and li_receiver = p_li_receiver; 368s if found then 368s return 1; 368s else 368s return 0; 368s end if; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropListen_int(p_li_origin int4, p_li_provider int4, p_li_receiver int4) is 368s 'dropListen (li_origin, li_provider, li_receiver) 368s 368s Process the DROP_LISTEN event, deleting the sl_listen entry for 368s the indicated (origin,provider,receiver) combination.'; 368s COMMENT 368s create or replace function public.storeSet (p_set_id int4, p_set_comment text) 368s returns bigint 368s as $$ 368s declare 368s v_local_node_id int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s v_local_node_id := public.getLocalNodeId('_main'); 368s 368s insert into public.sl_set 368s (set_id, set_origin, set_comment) values 368s (p_set_id, v_local_node_id, p_set_comment); 368s 368s return public.createEvent('_main', 'STORE_SET', 368s p_set_id::text, v_local_node_id::text, p_set_comment::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storeSet(p_set_id int4, p_set_comment text) is 368s 'Generate STORE_SET event for set set_id with human readable comment set_comment'; 368s COMMENT 368s create or replace function public.storeSet_int (p_set_id int4, p_set_origin int4, p_set_comment text) 368s returns int4 368s as $$ 368s declare 368s v_dummy int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s select 1 into v_dummy 368s from public.sl_set 368s where set_id = p_set_id 368s for update; 368s if found then 368s update public.sl_set 368s set set_comment = p_set_comment 368s where set_id = p_set_id; 368s else 368s if not exists (select 1 from public.sl_node 368s where no_id = p_set_origin) then 368s perform public.storeNode_int (p_set_origin, ''); 368s end if; 368s insert into public.sl_set 368s (set_id, set_origin, set_comment) values 368s (p_set_id, p_set_origin, p_set_comment); 368s end if; 368s 368s -- Run addPartialLogIndices() to try to add indices to unused sl_log_? table 368s perform public.addPartialLogIndices(); 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.storeSet_int(p_set_id int4, p_set_origin int4, p_set_comment text) is 368s 'storeSet_int (set_id, set_origin, set_comment) 368s 368s Process the STORE_SET event, indicating the new set with given ID, 368s origin node, and human readable comment.'; 368s COMMENT 368s create or replace function public.lockSet (p_set_id int4) 368s returns int4 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_set_row record; 368s v_tab_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that the set exists and that we are the origin 368s -- and that it is not already locked. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select * into v_set_row from public.sl_set 368s where set_id = p_set_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_set_row.set_origin <> v_local_node_id then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_set_id; 368s end if; 368s if v_set_row.set_locked notnull then 368s raise exception 'Slony-I: set % is already locked', p_set_id; 368s end if; 368s 368s -- ---- 368s -- Place the lockedSet trigger on all tables in the set. 368s -- ---- 368s for v_tab_row in select T.tab_id, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s from public.sl_table T, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where T.tab_set = p_set_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid 368s order by tab_id 368s loop 368s execute 'create trigger "_main_lockedset" ' || 368s 'before insert or update or delete on ' || 368s v_tab_row.tab_fqname || ' for each row execute procedure 368s public.lockedSet (''_main'');'; 368s end loop; 368s 368s -- ---- 368s -- Remember our snapshots xmax as for the set locking 368s -- ---- 368s update public.sl_set 368s set set_locked = "pg_catalog".txid_snapshot_xmax("pg_catalog".txid_current_snapshot()) 368s where set_id = p_set_id; 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.lockSet(p_set_id int4) is 368s 'lockSet(set_id) 368s 368s Add a special trigger to all tables of a set that disables access to 368s it.'; 368s COMMENT 368s create or replace function public.unlockSet (p_set_id int4) 368s returns int4 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_set_row record; 368s v_tab_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that the set exists and that we are the origin 368s -- and that it is not already locked. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select * into v_set_row from public.sl_set 368s where set_id = p_set_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_set_row.set_origin <> v_local_node_id then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_set_id; 368s end if; 368s if v_set_row.set_locked isnull then 368s raise exception 'Slony-I: set % is not locked', p_set_id; 368s end if; 368s 368s -- ---- 368s -- Drop the lockedSet trigger from all tables in the set. 368s -- ---- 368s for v_tab_row in select T.tab_id, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s from public.sl_table T, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where T.tab_set = p_set_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid 368s order by tab_id 368s loop 368s execute 'drop trigger "_main_lockedset" ' || 368s 'on ' || v_tab_row.tab_fqname; 368s end loop; 368s 368s -- ---- 368s -- Clear out the set_locked field 368s -- ---- 368s update public.sl_set 368s set set_locked = NULL 368s where set_id = p_set_id; 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.unlockSet(p_set_id int4) is 368s 'Remove the special trigger from all tables of a set that disables access to it.'; 368s COMMENT 368s create or replace function public.moveSet (p_set_id int4, p_new_origin int4) 368s returns bigint 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_set_row record; 368s v_sub_row record; 368s v_sync_seqno int8; 368s v_lv_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that the set is locked and that this locking 368s -- happened long enough ago. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select * into v_set_row from public.sl_set 368s where set_id = p_set_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_set_row.set_origin <> v_local_node_id then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_set_id; 368s end if; 368s if v_set_row.set_locked isnull then 368s raise exception 'Slony-I: set % is not locked', p_set_id; 368s end if; 368s if v_set_row.set_locked > "pg_catalog".txid_snapshot_xmin("pg_catalog".txid_current_snapshot()) then 368s raise exception 'Slony-I: cannot move set % yet, transactions < % are still in progress', 368s p_set_id, v_set_row.set_locked; 368s end if; 368s 368s -- ---- 368s -- Unlock the set 368s -- ---- 368s perform public.unlockSet(p_set_id); 368s 368s -- ---- 368s -- Check that the new_origin is an active subscriber of the set 368s -- ---- 368s select * into v_sub_row from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = p_new_origin; 368s if not found then 368s raise exception 'Slony-I: set % is not subscribed by node %', 368s p_set_id, p_new_origin; 368s end if; 368s if not v_sub_row.sub_active then 368s raise exception 'Slony-I: subsctiption of node % for set % is inactive', 368s p_new_origin, p_set_id; 368s end if; 368s 368s -- ---- 368s -- Reconfigure everything 368s -- ---- 368s perform public.moveSet_int(p_set_id, v_local_node_id, 368s p_new_origin, 0); 368s 368s perform public.RebuildListenEntries(); 368s 368s -- ---- 368s -- At this time we hold access exclusive locks for every table 368s -- in the set. But we did move the set to the new origin, so the 368s -- createEvent() we are doing now will not record the sequences. 368s -- ---- 368s v_sync_seqno := public.createEvent('_main', 'SYNC'); 368s insert into public.sl_seqlog 368s (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value) 368s select seq_id, v_local_node_id, v_sync_seqno, seq_last_value 368s from public.sl_seqlastvalue 368s where seq_set = p_set_id; 368s 368s -- ---- 368s -- Finally we generate the real event 368s -- ---- 368s return public.createEvent('_main', 'MOVE_SET', 368s p_set_id::text, v_local_node_id::text, p_new_origin::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.moveSet(p_set_id int4, p_new_origin int4) is 368s 'moveSet(set_id, new_origin) 368s 368s Generate MOVE_SET event to request that the origin for set set_id be moved to node new_origin'; 368s COMMENT 368s create or replace function public.moveSet_int (p_set_id int4, p_old_origin int4, p_new_origin int4, p_wait_seqno int8) 368s returns int4 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_tab_row record; 368s v_sub_row record; 368s v_sub_node int4; 368s v_sub_last int4; 368s v_sub_next int4; 368s v_last_sync int8; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get our local node ID 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s 368s -- On the new origin, raise an event - ACCEPT_SET 368s if v_local_node_id = p_new_origin then 368s -- Create a SYNC event as well so that the ACCEPT_SET has 368s -- the same snapshot as the last SYNC generated by the new 368s -- origin. This snapshot will be used by other nodes to 368s -- finalize the setsync status. 368s perform public.createEvent('_main', 'SYNC', NULL); 368s perform public.createEvent('_main', 'ACCEPT_SET', 368s p_set_id::text, p_old_origin::text, 368s p_new_origin::text, p_wait_seqno::text); 368s end if; 368s 368s -- ---- 368s -- Next we have to reverse the subscription path 368s -- ---- 368s v_sub_last = p_new_origin; 368s select sub_provider into v_sub_node 368s from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = p_new_origin; 368s if not found then 368s raise exception 'Slony-I: subscription path broken in moveSet_int'; 368s end if; 368s while v_sub_node <> p_old_origin loop 368s -- ---- 368s -- Tracing node by node, the old receiver is now in 368s -- v_sub_last and the old provider is in v_sub_node. 368s -- ---- 368s 368s -- ---- 368s -- Get the current provider of this node as next 368s -- and change the provider to the previous one in 368s -- the reverse chain. 368s -- ---- 368s select sub_provider into v_sub_next 368s from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = v_sub_node 368s for update; 368s if not found then 368s raise exception 'Slony-I: subscription path broken in moveSet_int'; 368s end if; 368s update public.sl_subscribe 368s set sub_provider = v_sub_last 368s where sub_set = p_set_id 368s and sub_receiver = v_sub_node 368s and sub_receiver <> v_sub_last; 368s 368s v_sub_last = v_sub_node; 368s v_sub_node = v_sub_next; 368s end loop; 368s 368s -- ---- 368s -- This includes creating a subscription for the old origin 368s -- ---- 368s insert into public.sl_subscribe 368s (sub_set, sub_provider, sub_receiver, 368s sub_forward, sub_active) 368s values (p_set_id, v_sub_last, p_old_origin, true, true); 368s if v_local_node_id = p_old_origin then 368s select coalesce(max(ev_seqno), 0) into v_last_sync 368s from public.sl_event 368s where ev_origin = p_new_origin 368s and ev_type = 'SYNC'; 368s if v_last_sync > 0 then 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s select p_set_id, p_new_origin, v_last_sync, 368s ev_snapshot, NULL 368s from public.sl_event 368s where ev_origin = p_new_origin 368s and ev_seqno = v_last_sync; 368s else 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s values (p_set_id, p_new_origin, '0', 368s '1:1:', NULL); 368s end if; 368s end if; 368s 368s -- ---- 368s -- Now change the ownership of the set. 368s -- ---- 368s update public.sl_set 368s set set_origin = p_new_origin 368s where set_id = p_set_id; 368s 368s -- ---- 368s -- On the new origin, delete the obsolete setsync information 368s -- and the subscription. 368s -- ---- 368s if v_local_node_id = p_new_origin then 368s delete from public.sl_setsync 368s where ssy_setid = p_set_id; 368s else 368s if v_local_node_id <> p_old_origin then 368s -- 368s -- On every other node, change the setsync so that it will 368s -- pick up from the new origins last known sync. 368s -- 368s delete from public.sl_setsync 368s where ssy_setid = p_set_id; 368s select coalesce(max(ev_seqno), 0) into v_last_sync 368s from public.sl_event 368s where ev_origin = p_new_origin 368s and ev_type = 'SYNC'; 368s if v_last_sync > 0 then 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s select p_set_id, p_new_origin, v_last_sync, 368s ev_snapshot, NULL 368s from public.sl_event 368s where ev_origin = p_new_origin 368s and ev_seqno = v_last_sync; 368s else 368s insert into public.sl_setsync 368s (ssy_setid, ssy_origin, ssy_seqno, 368s ssy_snapshot, ssy_action_list) 368s values (p_set_id, p_new_origin, 368s '0', '1:1:', NULL); 368s end if; 368s end if; 368s end if; 368s delete from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = p_new_origin; 368s 368s -- Regenerate sl_listen since we revised the subscriptions 368s perform public.RebuildListenEntries(); 368s 368s -- Run addPartialLogIndices() to try to add indices to unused sl_log_? table 368s perform public.addPartialLogIndices(); 368s 368s -- ---- 368s -- If we are the new or old origin, we have to 368s -- adjust the log and deny access trigger configuration. 368s -- ---- 368s if v_local_node_id = p_old_origin or v_local_node_id = p_new_origin then 368s for v_tab_row in select tab_id from public.sl_table 368s where tab_set = p_set_id 368s order by tab_id 368s loop 368s perform public.alterTableConfigureTriggers(v_tab_row.tab_id); 368s end loop; 368s end if; 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.moveSet_int(p_set_id int4, p_old_origin int4, p_new_origin int4, p_wait_seqno int8) is 368s 'moveSet(set_id, old_origin, new_origin, wait_seqno) 368s 368s Process MOVE_SET event to request that the origin for set set_id be 368s moved from old_origin to node new_origin'; 368s COMMENT 368s create or replace function public.dropSet (p_set_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that the set exists and originates here 368s -- ---- 368s select set_origin into v_origin from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_set_id; 368s end if; 368s 368s -- ---- 368s -- Call the internal drop set functionality and generate the event 368s -- ---- 368s perform public.dropSet_int(p_set_id); 368s return public.createEvent('_main', 'DROP_SET', 368s p_set_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropSet(p_set_id int4) is 368s 'Generate DROP_SET event to drop replication of set set_id'; 368s COMMENT 368s create or replace function public.dropSet_int (p_set_id int4) 368s returns int4 368s as $$ 368s declare 368s v_tab_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Restore all tables original triggers and rules and remove 368s -- our replication stuff. 368s -- ---- 368s for v_tab_row in select tab_id from public.sl_table 368s where tab_set = p_set_id 368s order by tab_id 368s loop 368s perform public.alterTableDropTriggers(v_tab_row.tab_id); 368s end loop; 368s 368s -- ---- 368s -- Remove all traces of the set configuration 368s -- ---- 368s delete from public.sl_sequence 368s where seq_set = p_set_id; 368s delete from public.sl_table 368s where tab_set = p_set_id; 368s delete from public.sl_subscribe 368s where sub_set = p_set_id; 368s delete from public.sl_setsync 368s where ssy_setid = p_set_id; 368s delete from public.sl_set 368s where set_id = p_set_id; 368s 368s -- Regenerate sl_listen since we revised the subscriptions 368s perform public.RebuildListenEntries(); 368s 368s -- Run addPartialLogIndices() to try to add indices to unused sl_log_? table 368s perform public.addPartialLogIndices(); 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.dropSet(p_set_id int4) is 368s 'Process DROP_SET event to drop replication of set set_id. This involves: 368s - Removing log and deny access triggers 368s - Removing all traces of the set configuration, including sequences, tables, subscribers, syncs, and the set itself'; 368s COMMENT 368s create or replace function public.mergeSet (p_set_id int4, p_add_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_origin int4; 368s in_progress boolean; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that both sets exist and originate here 368s -- ---- 368s if p_set_id = p_add_id then 368s raise exception 'Slony-I: merged set ids cannot be identical'; 368s end if; 368s select set_origin into v_origin from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_set_id; 368s end if; 368s 368s select set_origin into v_origin from public.sl_set 368s where set_id = p_add_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_add_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_add_id; 368s end if; 368s 368s -- ---- 368s -- Check that both sets are subscribed by the same set of nodes 368s -- ---- 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = p_set_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = p_add_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s p_set_id, p_add_id; 368s end if; 368s 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = p_add_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = p_set_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s p_add_id, p_set_id; 368s end if; 368s 368s -- ---- 368s -- Check that all ENABLE_SUBSCRIPTION events for the set are confirmed 368s -- ---- 368s select public.isSubscriptionInProgress(p_add_id) into in_progress ; 368s 368s if in_progress then 368s raise exception 'Slony-I: set % has subscriptions in progress - cannot merge', 368s p_add_id; 368s end if; 368s 368s -- ---- 368s -- Create a SYNC event, merge the sets, create a MERGE_SET event 368s -- ---- 368s perform public.createEvent('_main', 'SYNC', NULL); 368s perform public.mergeSet_int(p_set_id, p_add_id); 368s return public.createEvent('_main', 'MERGE_SET', 368s p_set_id::text, p_add_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.mergeSet(p_set_id int4, p_add_id int4) is 368s 'Generate MERGE_SET event to request that sets be merged together. 368s 368s Both sets must exist, and originate on the same node. They must be 368s subscribed by the same set of nodes.'; 368s COMMENT 368s create or replace function public.isSubscriptionInProgress(p_add_id int4) 368s returns boolean 368s as $$ 368s begin 368s if exists (select true from public.sl_event 368s where ev_type = 'ENABLE_SUBSCRIPTION' 368s and ev_data1 = p_add_id::text 368s and ev_seqno > (select max(con_seqno) from public.sl_confirm 368s where con_origin = ev_origin 368s and con_received::text = ev_data3)) 368s then 368s return true; 368s else 368s return false; 368s end if; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.isSubscriptionInProgress(p_add_id int4) is 368s 'Checks to see if a subscription for the indicated set is in progress. 368s Returns true if a subscription is in progress. Otherwise false'; 368s COMMENT 368s create or replace function public.mergeSet_int (p_set_id int4, p_add_id int4) 368s returns int4 368s as $$ 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s update public.sl_sequence 368s set seq_set = p_set_id 368s where seq_set = p_add_id; 368s update public.sl_table 368s set tab_set = p_set_id 368s where tab_set = p_add_id; 368s delete from public.sl_subscribe 368s where sub_set = p_add_id; 368s delete from public.sl_setsync 368s where ssy_setid = p_add_id; 368s delete from public.sl_set 368s where set_id = p_add_id; 368s 368s return p_set_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.mergeSet_int(p_set_id int4, p_add_id int4) is 368s 'mergeSet_int(set_id, add_id) - Perform MERGE_SET event, merging all objects from 368s set add_id into set set_id.'; 368s COMMENT 368s create or replace function public.setAddTable(p_set_id int4, p_tab_id int4, p_fqname text, p_tab_idxname name, p_tab_comment text) 368s returns bigint 368s as $$ 368s declare 368s v_set_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that we are the origin of the set 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: setAddTable(): set % not found', p_set_id; 368s end if; 368s if v_set_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: setAddTable(): set % has remote origin', p_set_id; 368s end if; 368s 368s if exists (select true from public.sl_subscribe 368s where sub_set = p_set_id) 368s then 368s raise exception 'Slony-I: cannot add table to currently subscribed set % - must attach to an unsubscribed set', 368s p_set_id; 368s end if; 368s 368s -- ---- 368s -- Add the table to the set and generate the SET_ADD_TABLE event 368s -- ---- 368s perform public.setAddTable_int(p_set_id, p_tab_id, p_fqname, 368s p_tab_idxname, p_tab_comment); 368s return public.createEvent('_main', 'SET_ADD_TABLE', 368s p_set_id::text, p_tab_id::text, p_fqname::text, 368s p_tab_idxname::text, p_tab_comment::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setAddTable(p_set_id int4, p_tab_id int4, p_fqname text, p_tab_idxname name, p_tab_comment text) is 368s 'setAddTable (set_id, tab_id, tab_fqname, tab_idxname, tab_comment) 368s 368s Add table tab_fqname to replication set on origin node, and generate 368s SET_ADD_TABLE event to allow this to propagate to other nodes. 368s 368s Note that the table id, tab_id, must be unique ACROSS ALL SETS.'; 368s COMMENT 368s create or replace function public.setAddTable_int(p_set_id int4, p_tab_id int4, p_fqname text, p_tab_idxname name, p_tab_comment text) 368s returns int4 368s as $$ 368s declare 368s v_tab_relname name; 368s v_tab_nspname name; 368s v_local_node_id int4; 368s v_set_origin int4; 368s v_sub_provider int4; 368s v_relkind char; 368s v_tab_reloid oid; 368s v_pkcand_nn boolean; 368s v_prec record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- For sets with a remote origin, check that we are subscribed 368s -- to that set. Otherwise we ignore the table because it might 368s -- not even exist in our database. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: setAddTable_int(): set % not found', 368s p_set_id; 368s end if; 368s if v_set_origin != v_local_node_id then 368s select sub_provider into v_sub_provider 368s from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = public.getLocalNodeId('_main'); 368s if not found then 368s return 0; 368s end if; 368s end if; 368s 368s -- ---- 368s -- Get the tables OID and check that it is a real table 368s -- ---- 368s select PGC.oid, PGC.relkind, PGC.relname, PGN.nspname into v_tab_reloid, v_relkind, v_tab_relname, v_tab_nspname 368s from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where PGC.relnamespace = PGN.oid 368s and public.slon_quote_input(p_fqname) = public.slon_quote_brute(PGN.nspname) || 368s '.' || public.slon_quote_brute(PGC.relname); 368s if not found then 368s raise exception 'Slony-I: setAddTable_int(): table % not found', 368s p_fqname; 368s end if; 368s if v_relkind != 'r' then 368s raise exception 'Slony-I: setAddTable_int(): % is not a regular table', 368s p_fqname; 368s end if; 368s 368s if not exists (select indexrelid 368s from "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGC 368s where PGX.indrelid = v_tab_reloid 368s and PGX.indexrelid = PGC.oid 368s and PGC.relname = p_tab_idxname) 368s then 368s raise exception 'Slony-I: setAddTable_int(): table % has no index %', 368s p_fqname, p_tab_idxname; 368s end if; 368s 368s -- ---- 368s -- Verify that the columns in the PK (or candidate) are not NULLABLE 368s -- ---- 368s 368s v_pkcand_nn := 'f'; 368s for v_prec in select attname from "pg_catalog".pg_attribute where attrelid = 368s (select oid from "pg_catalog".pg_class where oid = v_tab_reloid) 368s and attname in (select attname from "pg_catalog".pg_attribute where 368s attrelid = (select oid from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_index PGX where 368s PGC.relname = p_tab_idxname and PGX.indexrelid=PGC.oid and 368s PGX.indrelid = v_tab_reloid)) and attnotnull <> 't' 368s loop 368s raise notice 'Slony-I: setAddTable_int: table % PK column % nullable', p_fqname, v_prec.attname; 368s v_pkcand_nn := 't'; 368s end loop; 368s if v_pkcand_nn then 368s raise exception 'Slony-I: setAddTable_int: table % not replicable!', p_fqname; 368s end if; 368s 368s select * into v_prec from public.sl_table where tab_id = p_tab_id; 368s if not found then 368s v_pkcand_nn := 't'; -- No-op -- All is well 368s else 368s raise exception 'Slony-I: setAddTable_int: table id % has already been assigned!', p_tab_id; 368s end if; 368s 368s -- ---- 368s -- Add the table to sl_table and create the trigger on it. 368s -- ---- 368s insert into public.sl_table 368s (tab_id, tab_reloid, tab_relname, tab_nspname, 368s tab_set, tab_idxname, tab_altered, tab_comment) 368s values 368s (p_tab_id, v_tab_reloid, v_tab_relname, v_tab_nspname, 368s p_set_id, p_tab_idxname, false, p_tab_comment); 368s perform public.alterTableAddTriggers(p_tab_id); 368s 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setAddTable_int(p_set_id int4, p_tab_id int4, p_fqname text, p_tab_idxname name, p_tab_comment text) is 368s 'setAddTable_int (set_id, tab_id, tab_fqname, tab_idxname, tab_comment) 368s 368s This function processes the SET_ADD_TABLE event on remote nodes, 368s adding a table to replication if the remote node is subscribing to its 368s replication set.'; 368s COMMENT 368s create or replace function public.setDropTable(p_tab_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_set_id int4; 368s v_set_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Determine the set_id 368s -- ---- 368s select tab_set into v_set_id from public.sl_table where tab_id = p_tab_id; 368s 368s -- ---- 368s -- Ensure table exists 368s -- ---- 368s if not found then 368s raise exception 'Slony-I: setDropTable_int(): table % not found', 368s p_tab_id; 368s end if; 368s 368s -- ---- 368s -- Check that we are the origin of the set 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = v_set_id; 368s if not found then 368s raise exception 'Slony-I: setDropTable(): set % not found', v_set_id; 368s end if; 368s if v_set_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: setDropTable(): set % has remote origin', v_set_id; 368s end if; 368s 368s -- ---- 368s -- Drop the table from the set and generate the SET_ADD_TABLE event 368s -- ---- 368s perform public.setDropTable_int(p_tab_id); 368s return public.createEvent('_main', 'SET_DROP_TABLE', 368s p_tab_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setDropTable(p_tab_id int4) is 368s 'setDropTable (tab_id) 368s 368s Drop table tab_id from set on origin node, and generate SET_DROP_TABLE 368s event to allow this to propagate to other nodes.'; 368s COMMENT 368s create or replace function public.setDropTable_int(p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_set_id int4; 368s v_local_node_id int4; 368s v_set_origin int4; 368s v_sub_provider int4; 368s v_tab_reloid oid; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Determine the set_id 368s -- ---- 368s select tab_set into v_set_id from public.sl_table where tab_id = p_tab_id; 368s 368s -- ---- 368s -- Ensure table exists 368s -- ---- 368s if not found then 368s return 0; 368s end if; 368s 368s -- ---- 368s -- For sets with a remote origin, check that we are subscribed 368s -- to that set. Otherwise we ignore the table because it might 368s -- not even exist in our database. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = v_set_id; 368s if not found then 368s raise exception 'Slony-I: setDropTable_int(): set % not found', 368s v_set_id; 368s end if; 368s if v_set_origin != v_local_node_id then 368s select sub_provider into v_sub_provider 368s from public.sl_subscribe 368s where sub_set = v_set_id 368s and sub_receiver = public.getLocalNodeId('_main'); 368s if not found then 368s return 0; 368s end if; 368s end if; 368s 368s -- ---- 368s -- Drop the table from sl_table and drop trigger from it. 368s -- ---- 368s perform public.alterTableDropTriggers(p_tab_id); 368s delete from public.sl_table where tab_id = p_tab_id; 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setDropTable_int(p_tab_id int4) is 368s 'setDropTable_int (tab_id) 368s 368s This function processes the SET_DROP_TABLE event on remote nodes, 368s dropping a table from replication if the remote node is subscribing to 368s its replication set.'; 368s COMMENT 368s create or replace function public.setAddSequence (p_set_id int4, p_seq_id int4, p_fqname text, p_seq_comment text) 368s returns bigint 368s as $$ 368s declare 368s v_set_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that we are the origin of the set 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: setAddSequence(): set % not found', p_set_id; 368s end if; 368s if v_set_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: setAddSequence(): set % has remote origin - submit to origin node', p_set_id; 368s end if; 368s 368s if exists (select true from public.sl_subscribe 368s where sub_set = p_set_id) 368s then 368s raise exception 'Slony-I: cannot add sequence to currently subscribed set %', 368s p_set_id; 368s end if; 368s 368s -- ---- 368s -- Add the sequence to the set and generate the SET_ADD_SEQUENCE event 368s -- ---- 368s perform public.setAddSequence_int(p_set_id, p_seq_id, p_fqname, 368s p_seq_comment); 368s return public.createEvent('_main', 'SET_ADD_SEQUENCE', 368s p_set_id::text, p_seq_id::text, 368s p_fqname::text, p_seq_comment::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setAddSequence (p_set_id int4, p_seq_id int4, p_fqname text, p_seq_comment text) is 368s 'setAddSequence (set_id, seq_id, seq_fqname, seq_comment) 368s 368s On the origin node for set set_id, add sequence seq_fqname to the 368s replication set, and raise SET_ADD_SEQUENCE to cause this to replicate 368s to subscriber nodes.'; 368s COMMENT 368s create or replace function public.setAddSequence_int(p_set_id int4, p_seq_id int4, p_fqname text, p_seq_comment text) 368s returns int4 368s as $$ 368s declare 368s v_local_node_id int4; 368s v_set_origin int4; 368s v_sub_provider int4; 368s v_relkind char; 368s v_seq_reloid oid; 368s v_seq_relname name; 368s v_seq_nspname name; 368s v_sync_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- For sets with a remote origin, check that we are subscribed 368s -- to that set. Otherwise we ignore the sequence because it might 368s -- not even exist in our database. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_set_id; 368s if not found then 368s raise exception 'Slony-I: setAddSequence_int(): set % not found', 368s p_set_id; 368s end if; 368s if v_set_origin != v_local_node_id then 368s select sub_provider into v_sub_provider 368s from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = public.getLocalNodeId('_main'); 368s if not found then 368s return 0; 368s end if; 368s end if; 368s 368s -- ---- 368s -- Get the sequences OID and check that it is a sequence 368s -- ---- 368s select PGC.oid, PGC.relkind, PGC.relname, PGN.nspname 368s into v_seq_reloid, v_relkind, v_seq_relname, v_seq_nspname 368s from "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where PGC.relnamespace = PGN.oid 368s and public.slon_quote_input(p_fqname) = public.slon_quote_brute(PGN.nspname) || 368s '.' || public.slon_quote_brute(PGC.relname); 368s if not found then 368s raise exception 'Slony-I: setAddSequence_int(): sequence % not found', 368s p_fqname; 368s end if; 368s if v_relkind != 'S' then 368s raise exception 'Slony-I: setAddSequence_int(): % is not a sequence', 368s p_fqname; 368s end if; 368s 368s select 1 into v_sync_row from public.sl_sequence where seq_id = p_seq_id; 368s if not found then 368s v_relkind := 'o'; -- all is OK 368s else 368s raise exception 'Slony-I: setAddSequence_int(): sequence ID % has already been assigned', p_seq_id; 368s end if; 368s 368s -- ---- 368s -- Add the sequence to sl_sequence 368s -- ---- 368s insert into public.sl_sequence 368s (seq_id, seq_reloid, seq_relname, seq_nspname, seq_set, seq_comment) 368s values 368s (p_seq_id, v_seq_reloid, v_seq_relname, v_seq_nspname, p_set_id, p_seq_comment); 368s 368s -- ---- 368s -- On the set origin, fake a sl_seqlog row for the last sync event 368s -- ---- 368s if v_set_origin = v_local_node_id then 368s for v_sync_row in select coalesce (max(ev_seqno), 0) as ev_seqno 368s from public.sl_event 368s where ev_origin = v_local_node_id 368s and ev_type = 'SYNC' 368s loop 368s insert into public.sl_seqlog 368s (seql_seqid, seql_origin, seql_ev_seqno, 368s seql_last_value) values 368s (p_seq_id, v_local_node_id, v_sync_row.ev_seqno, 368s public.sequenceLastValue(p_fqname)); 368s end loop; 368s end if; 368s 368s return p_seq_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setAddSequence_int(p_set_id int4, p_seq_id int4, p_fqname text, p_seq_comment text) is 368s 'setAddSequence_int (set_id, seq_id, seq_fqname, seq_comment) 368s 368s This processes the SET_ADD_SEQUENCE event. On remote nodes that 368s subscribe to set_id, add the sequence to the replication set.'; 368s COMMENT 368s create or replace function public.setDropSequence (p_seq_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_set_id int4; 368s v_set_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Determine set id for this sequence 368s -- ---- 368s select seq_set into v_set_id from public.sl_sequence where seq_id = p_seq_id; 368s 368s -- ---- 368s -- Ensure sequence exists 368s -- ---- 368s if not found then 368s raise exception 'Slony-I: setDropSequence_int(): sequence % not found', 368s p_seq_id; 368s end if; 368s 368s -- ---- 368s -- Check that we are the origin of the set 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = v_set_id; 368s if not found then 368s raise exception 'Slony-I: setDropSequence(): set % not found', v_set_id; 368s end if; 368s if v_set_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: setDropSequence(): set % has origin at another node - submit this to that node', v_set_id; 368s end if; 368s 368s -- ---- 368s -- Add the sequence to the set and generate the SET_ADD_SEQUENCE event 368s -- ---- 368s perform public.setDropSequence_int(p_seq_id); 368s return public.createEvent('_main', 'SET_DROP_SEQUENCE', 368s p_seq_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setDropSequence (p_seq_id int4) is 368s 'setDropSequence (seq_id) 368s 368s On the origin node for the set, drop sequence seq_id from replication 368s set, and raise SET_DROP_SEQUENCE to cause this to replicate to 368s subscriber nodes.'; 368s COMMENT 368s create or replace function public.setDropSequence_int(p_seq_id int4) 368s returns int4 368s as $$ 368s declare 368s v_set_id int4; 368s v_local_node_id int4; 368s v_set_origin int4; 368s v_sub_provider int4; 368s v_relkind char; 368s v_sync_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Determine set id for this sequence 368s -- ---- 368s select seq_set into v_set_id from public.sl_sequence where seq_id = p_seq_id; 368s 368s -- ---- 368s -- Ensure sequence exists 368s -- ---- 368s if not found then 368s return 0; 368s end if; 368s 368s -- ---- 368s -- For sets with a remote origin, check that we are subscribed 368s -- to that set. Otherwise we ignore the sequence because it might 368s -- not even exist in our database. 368s -- ---- 368s v_local_node_id := public.getLocalNodeId('_main'); 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = v_set_id; 368s if not found then 368s raise exception 'Slony-I: setDropSequence_int(): set % not found', 368s v_set_id; 368s end if; 368s if v_set_origin != v_local_node_id then 368s select sub_provider into v_sub_provider 368s from public.sl_subscribe 368s where sub_set = v_set_id 368s and sub_receiver = public.getLocalNodeId('_main'); 368s if not found then 368s return 0; 368s end if; 368s end if; 368s 368s -- ---- 368s -- drop the sequence from sl_sequence, sl_seqlog 368s -- ---- 368s delete from public.sl_seqlog where seql_seqid = p_seq_id; 368s delete from public.sl_sequence where seq_id = p_seq_id; 368s 368s return p_seq_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setDropSequence_int(p_seq_id int4) is 368s 'setDropSequence_int (seq_id) 368s 368s This processes the SET_DROP_SEQUENCE event. On remote nodes that 368s subscribe to the set containing sequence seq_id, drop the sequence 368s from the replication set.'; 368s COMMENT 368s create or replace function public.setMoveTable (p_tab_id int4, p_new_set_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_old_set_id int4; 368s v_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get the tables current set 368s -- ---- 368s select tab_set into v_old_set_id from public.sl_table 368s where tab_id = p_tab_id; 368s if not found then 368s raise exception 'Slony-I: table %d not found', p_tab_id; 368s end if; 368s 368s -- ---- 368s -- Check that both sets exist and originate here 368s -- ---- 368s if p_new_set_id = v_old_set_id then 368s raise exception 'Slony-I: set ids cannot be identical'; 368s end if; 368s select set_origin into v_origin from public.sl_set 368s where set_id = p_new_set_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_new_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s p_new_set_id; 368s end if; 368s 368s select set_origin into v_origin from public.sl_set 368s where set_id = v_old_set_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', v_old_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s v_old_set_id; 368s end if; 368s 368s -- ---- 368s -- Check that both sets are subscribed by the same set of nodes 368s -- ---- 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = p_new_set_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = v_old_set_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s p_new_set_id, v_old_set_id; 368s end if; 368s 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = v_old_set_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = p_new_set_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s v_old_set_id, p_new_set_id; 368s end if; 368s 368s -- ---- 368s -- Change the set the table belongs to 368s -- ---- 368s perform public.createEvent('_main', 'SYNC', NULL); 368s perform public.setMoveTable_int(p_tab_id, p_new_set_id); 368s return public.createEvent('_main', 'SET_MOVE_TABLE', 368s p_tab_id::text, p_new_set_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setMoveTable(p_tab_id int4, p_new_set_id int4) is 368s 'This generates the SET_MOVE_TABLE event. If the set that the table is 368s in is identically subscribed to the set that the table is to be moved 368s into, then the SET_MOVE_TABLE event is raised.'; 368s COMMENT 368s create or replace function public.setMoveTable_int (p_tab_id int4, p_new_set_id int4) 368s returns int4 368s as $$ 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Move the table to the new set 368s -- ---- 368s update public.sl_table 368s set tab_set = p_new_set_id 368s where tab_id = p_tab_id; 368s 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setMoveTable(p_tab_id int4, p_new_set_id int4) is 368s 'This processes the SET_MOVE_TABLE event. The table is moved 368s to the destination set.'; 368s COMMENT 368s create or replace function public.setMoveSequence (p_seq_id int4, p_new_set_id int4) 368s returns bigint 368s as $$ 368s declare 368s v_old_set_id int4; 368s v_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get the sequences current set 368s -- ---- 368s select seq_set into v_old_set_id from public.sl_sequence 368s where seq_id = p_seq_id; 368s if not found then 368s raise exception 'Slony-I: setMoveSequence(): sequence %d not found', p_seq_id; 368s end if; 368s 368s -- ---- 368s -- Check that both sets exist and originate here 368s -- ---- 368s if p_new_set_id = v_old_set_id then 368s raise exception 'Slony-I: setMoveSequence(): set ids cannot be identical'; 368s end if; 368s select set_origin into v_origin from public.sl_set 368s where set_id = p_new_set_id; 368s if not found then 368s raise exception 'Slony-I: setMoveSequence(): set % not found', p_new_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: setMoveSequence(): set % does not originate on local node', 368s p_new_set_id; 368s end if; 368s 368s select set_origin into v_origin from public.sl_set 368s where set_id = v_old_set_id; 368s if not found then 368s raise exception 'Slony-I: set % not found', v_old_set_id; 368s end if; 368s if v_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: set % does not originate on local node', 368s v_old_set_id; 368s end if; 368s 368s -- ---- 368s -- Check that both sets are subscribed by the same set of nodes 368s -- ---- 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = p_new_set_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = v_old_set_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s p_new_set_id, v_old_set_id; 368s end if; 368s 368s if exists (select true from public.sl_subscribe SUB1 368s where SUB1.sub_set = v_old_set_id 368s and SUB1.sub_receiver not in (select SUB2.sub_receiver 368s from public.sl_subscribe SUB2 368s where SUB2.sub_set = p_new_set_id)) 368s then 368s raise exception 'Slony-I: subscriber lists of set % and % are different', 368s v_old_set_id, p_new_set_id; 368s end if; 368s 368s -- ---- 368s -- Change the set the sequence belongs to 368s -- ---- 368s perform public.setMoveSequence_int(p_seq_id, p_new_set_id); 368s return public.createEvent('_main', 'SET_MOVE_SEQUENCE', 368s p_seq_id::text, p_new_set_id::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setMoveSequence (p_seq_id int4, p_new_set_id int4) is 368s 'setMoveSequence(p_seq_id, p_new_set_id) - This generates the 368s SET_MOVE_SEQUENCE event, after validation, notably that both sets 368s exist, are distinct, and have exactly the same subscription lists'; 368s COMMENT 368s create or replace function public.setMoveSequence_int (p_seq_id int4, p_new_set_id int4) 368s returns int4 368s as $$ 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Move the sequence to the new set 368s -- ---- 368s update public.sl_sequence 368s set seq_set = p_new_set_id 368s where seq_id = p_seq_id; 368s 368s return p_seq_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setMoveSequence_int (p_seq_id int4, p_new_set_id int4) is 368s 'setMoveSequence_int(p_seq_id, p_new_set_id) - processes the 368s SET_MOVE_SEQUENCE event, moving a sequence to another replication 368s set.'; 368s COMMENT 368s create or replace function public.sequenceSetValue(p_seq_id int4, p_seq_origin int4, p_ev_seqno int8, p_last_value int8,p_ignore_missing bool) returns int4 368s as $$ 368s declare 368s v_fqname text; 368s v_found integer; 368s begin 368s -- ---- 368s -- Get the sequences fully qualified name 368s -- ---- 368s select public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) into v_fqname 368s from public.sl_sequence SQ, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where SQ.seq_id = p_seq_id 368s and SQ.seq_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid; 368s if not found then 368s if p_ignore_missing then 368s return null; 368s end if; 368s raise exception 'Slony-I: sequenceSetValue(): sequence % not found', p_seq_id; 368s end if; 368s 368s -- ---- 368s -- Update it to the new value 368s -- ---- 368s execute 'select setval(''' || v_fqname || 368s ''', ' || p_last_value::text || ')'; 368s 368s if p_ev_seqno is not null then 368s insert into public.sl_seqlog 368s (seql_seqid, seql_origin, seql_ev_seqno, seql_last_value) 368s values (p_seq_id, p_seq_origin, p_ev_seqno, p_last_value); 368s end if; 368s return p_seq_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.sequenceSetValue(p_seq_id int4, p_seq_origin int4, p_ev_seqno int8, p_last_value int8,p_ignore_missing bool) is 368s 'sequenceSetValue (seq_id, seq_origin, ev_seqno, last_value,ignore_missing) 368s Set sequence seq_id to have new value last_value. 368s '; 368s COMMENT 368s drop function if exists public.ddlCapture (p_statement text, p_nodes text); 368s DROP FUNCTION 368s create or replace function public.ddlCapture (p_statement text, p_nodes text) 368s returns bigint 368s as $$ 368s declare 368s c_local_node integer; 368s c_found_origin boolean; 368s c_node text; 368s c_cmdargs text[]; 368s c_nodeargs text; 368s c_delim text; 368s begin 368s c_local_node := public.getLocalNodeId('_main'); 368s 368s c_cmdargs = array_append('{}'::text[], p_statement); 368s c_nodeargs = ''; 368s if p_nodes is not null then 368s c_found_origin := 'f'; 368s -- p_nodes list needs to consist of a list of nodes that exist 368s -- and that include the current node ID 368s for c_node in select trim(node) from 368s pg_catalog.regexp_split_to_table(p_nodes, ',') as node loop 368s if not exists 368s (select 1 from public.sl_node 368s where no_id = (c_node::integer)) then 368s raise exception 'ddlcapture(%,%) - node % does not exist!', 368s p_statement, p_nodes, c_node; 368s end if; 368s 368s if c_local_node = (c_node::integer) then 368s c_found_origin := 't'; 368s end if; 368s if length(c_nodeargs)>0 then 368s c_nodeargs = c_nodeargs ||','|| c_node; 368s else 368s c_nodeargs=c_node; 368s end if; 368s end loop; 368s 368s if not c_found_origin then 368s raise exception 368s 'ddlcapture(%,%) - origin node % not included in ONLY ON list!', 368s p_statement, p_nodes, c_local_node; 368s end if; 368s end if; 368s c_cmdargs = array_append(c_cmdargs,c_nodeargs); 368s c_delim=','; 368s c_cmdargs = array_append(c_cmdargs, 368s 368s (select public.string_agg( seq_id::text || c_delim 368s || c_local_node || 368s c_delim || seq_last_value) 368s FROM ( 368s select seq_id, 368s seq_last_value from public.sl_seqlastvalue 368s where seq_origin = c_local_node) as FOO 368s where NOT public.seqtrack(seq_id,seq_last_value) is NULL)); 368s insert into public.sl_log_script 368s (log_origin, log_txid, log_actionseq, log_cmdtype, log_cmdargs) 368s values 368s (c_local_node, pg_catalog.txid_current(), 368s nextval('public.sl_action_seq'), 'S', c_cmdargs); 368s execute p_statement; 368s return currval('public.sl_action_seq'); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.ddlCapture (p_statement text, p_nodes text) is 368s 'Capture an SQL statement (usually DDL) that is to be literally replayed on subscribers'; 368s COMMENT 368s drop function if exists public.ddlScript_complete (int4, text, int4); 368s DROP FUNCTION 368s create or replace function public.ddlScript_complete (p_nodes text) 368s returns bigint 368s as $$ 368s declare 368s c_local_node integer; 368s c_found_origin boolean; 368s c_node text; 368s c_cmdargs text[]; 368s begin 368s c_local_node := public.getLocalNodeId('_main'); 368s 368s c_cmdargs = '{}'::text[]; 368s if p_nodes is not null then 368s c_found_origin := 'f'; 368s -- p_nodes list needs to consist o a list of nodes that exist 368s -- and that include the current node ID 368s for c_node in select trim(node) from 368s pg_catalog.regexp_split_to_table(p_nodes, ',') as node loop 368s if not exists 368s (select 1 from public.sl_node 368s where no_id = (c_node::integer)) then 368s raise exception 'ddlcapture(%,%) - node % does not exist!', 368s p_statement, p_nodes, c_node; 368s end if; 368s 368s if c_local_node = (c_node::integer) then 368s c_found_origin := 't'; 368s end if; 368s 368s c_cmdargs = array_append(c_cmdargs, c_node); 368s end loop; 368s 368s if not c_found_origin then 368s raise exception 368s 'ddlScript_complete(%) - origin node % not included in ONLY ON list!', 368s p_nodes, c_local_node; 368s end if; 368s end if; 368s 368s perform public.ddlScript_complete_int(); 368s 368s insert into public.sl_log_script 368s (log_origin, log_txid, log_actionseq, log_cmdtype, log_cmdargs) 368s values 368s (c_local_node, pg_catalog.txid_current(), 368s nextval('public.sl_action_seq'), 's', c_cmdargs); 368s 368s return currval('public.sl_action_seq'); 368s end; 368s $$ language plpgsql; 368s NOTICE: function public.ddlcapture(text,text) does not exist, skipping 368s NOTICE: function public.ddlscript_complete(int4,text,int4) does not exist, skipping 368s NOTICE: function public.ddlscript_complete_int(int4,int4) does not exist, skipping 368s NOTICE: function public.subscribeset_int(int4,int4,int4,bool,bool) does not exist, skipping 368s NOTICE: function public.unsubscribeset(int4,int4,pg_catalog.bool) does not exist, skipping 368s NOTICE: function public.updaterelname(int4,int4) does not exist, skipping 368s NOTICE: function public.updatereloid(int4,int4) does not exist, skipping 368s CREATE FUNCTION 368s comment on function public.ddlScript_complete(p_nodes text) is 368s 'ddlScript_complete(set_id, script, only_on_node) 368s 368s After script has run on origin, this fixes up relnames and 368s log trigger arguments and inserts the "fire ddlScript_complete_int() 368s log row into sl_log_script.'; 368s COMMENT 368s drop function if exists public.ddlScript_complete_int(int4, int4); 368s DROP FUNCTION 368s create or replace function public.ddlScript_complete_int () 368s returns int4 368s as $$ 368s begin 368s perform public.updateRelname(); 368s perform public.repair_log_triggers(true); 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.ddlScript_complete_int() is 368s 'ddlScript_complete_int() 368s 368s Complete processing the DDL_SCRIPT event.'; 368s COMMENT 368s create or replace function public.alterTableAddTriggers (p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_no_id int4; 368s v_tab_row record; 368s v_tab_fqname text; 368s v_tab_attkind text; 368s v_n int4; 368s v_trec record; 368s v_tgbad boolean; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get our local node ID 368s -- ---- 368s v_no_id := public.getLocalNodeId('_main'); 368s 368s -- ---- 368s -- Get the sl_table row and the current origin of the table. 368s -- ---- 368s select T.tab_reloid, T.tab_set, T.tab_idxname, 368s S.set_origin, PGX.indexrelid, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s into v_tab_row 368s from public.sl_table T, public.sl_set S, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC 368s where T.tab_id = p_tab_id 368s and T.tab_set = S.set_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid 368s and PGX.indrelid = T.tab_reloid 368s and PGX.indexrelid = PGXC.oid 368s and PGXC.relname = T.tab_idxname 368s for update; 368s if not found then 368s raise exception 'Slony-I: alterTableAddTriggers(): Table with id % not found', p_tab_id; 368s end if; 368s v_tab_fqname = v_tab_row.tab_fqname; 368s 368s v_tab_attkind := public.determineAttKindUnique(v_tab_row.tab_fqname, 368s v_tab_row.tab_idxname); 368s 368s execute 'lock table ' || v_tab_fqname || ' in access exclusive mode'; 368s 368s -- ---- 368s -- Create the log and the deny access triggers 368s -- ---- 368s execute 'create trigger "_main_logtrigger"' || 368s ' after insert or update or delete on ' || 368s v_tab_fqname || ' for each row execute procedure public.logTrigger (' || 368s pg_catalog.quote_literal('_main') || ',' || 368s pg_catalog.quote_literal(p_tab_id::text) || ',' || 368s pg_catalog.quote_literal(v_tab_attkind) || ');'; 368s 368s execute 'create trigger "_main_denyaccess" ' || 368s 'before insert or update or delete on ' || 368s v_tab_fqname || ' for each row execute procedure ' || 368s 'public.denyAccess (' || pg_catalog.quote_literal('_main') || ');'; 368s 368s perform public.alterTableAddTruncateTrigger(v_tab_fqname, p_tab_id); 368s 368s perform public.alterTableConfigureTriggers (p_tab_id); 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.alterTableAddTriggers(p_tab_id int4) is 368s 'alterTableAddTriggers(tab_id) 368s 368s Adds the log and deny access triggers to a replicated table.'; 368s COMMENT 368s create or replace function public.alterTableDropTriggers (p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_no_id int4; 368s v_tab_row record; 368s v_tab_fqname text; 368s v_n int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get our local node ID 368s -- ---- 368s v_no_id := public.getLocalNodeId('_main'); 368s 368s -- ---- 368s -- Get the sl_table row and the current tables origin. 368s -- ---- 368s select T.tab_reloid, T.tab_set, 368s S.set_origin, PGX.indexrelid, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s into v_tab_row 368s from public.sl_table T, public.sl_set S, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC 368s where T.tab_id = p_tab_id 368s and T.tab_set = S.set_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid 368s and PGX.indrelid = T.tab_reloid 368s and PGX.indexrelid = PGXC.oid 368s and PGXC.relname = T.tab_idxname 368s for update; 368s if not found then 368s raise exception 'Slony-I: alterTableDropTriggers(): Table with id % not found', p_tab_id; 368s end if; 368s v_tab_fqname = v_tab_row.tab_fqname; 368s 368s execute 'lock table ' || v_tab_fqname || ' in access exclusive mode'; 368s 368s -- ---- 368s -- Drop both triggers 368s -- ---- 368s execute 'drop trigger "_main_logtrigger" on ' || 368s v_tab_fqname; 368s 368s execute 'drop trigger "_main_denyaccess" on ' || 368s v_tab_fqname; 368s 368s perform public.alterTableDropTruncateTrigger(v_tab_fqname, p_tab_id); 368s 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.alterTableDropTriggers (p_tab_id int4) is 368s 'alterTableDropTriggers (tab_id) 368s 368s Remove the log and deny access triggers from a table.'; 368s COMMENT 368s create or replace function public.alterTableConfigureTriggers (p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_no_id int4; 368s v_tab_row record; 368s v_tab_fqname text; 368s v_n int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Get our local node ID 368s -- ---- 368s v_no_id := public.getLocalNodeId('_main'); 368s 368s -- ---- 368s -- Get the sl_table row and the current tables origin. 368s -- ---- 368s select T.tab_reloid, T.tab_set, 368s S.set_origin, PGX.indexrelid, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s into v_tab_row 368s from public.sl_table T, public.sl_set S, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, "pg_catalog".pg_class PGXC 368s where T.tab_id = p_tab_id 368s and T.tab_set = S.set_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid 368s and PGX.indrelid = T.tab_reloid 368s and PGX.indexrelid = PGXC.oid 368s and PGXC.relname = T.tab_idxname 368s for update; 368s if not found then 368s raise exception 'Slony-I: alterTableConfigureTriggers(): Table with id % not found', p_tab_id; 368s end if; 368s v_tab_fqname = v_tab_row.tab_fqname; 368s 368s -- ---- 368s -- Configuration depends on the origin of the table 368s -- ---- 368s if v_tab_row.set_origin = v_no_id then 368s -- ---- 368s -- On the origin the log trigger is configured like a default 368s -- user trigger and the deny access trigger is disabled. 368s -- ---- 368s execute 'alter table ' || v_tab_fqname || 368s ' enable trigger "_main_logtrigger"'; 368s execute 'alter table ' || v_tab_fqname || 368s ' disable trigger "_main_denyaccess"'; 368s perform public.alterTableConfigureTruncateTrigger(v_tab_fqname, 368s 'enable', 'disable'); 368s else 368s -- ---- 368s -- On a replica the log trigger is disabled and the 368s -- deny access trigger fires in origin session role. 368s -- ---- 368s execute 'alter table ' || v_tab_fqname || 368s ' disable trigger "_main_logtrigger"'; 368s execute 'alter table ' || v_tab_fqname || 368s ' enable trigger "_main_denyaccess"'; 368s perform public.alterTableConfigureTruncateTrigger(v_tab_fqname, 368s 'disable', 'enable'); 368s 368s end if; 368s 368s return p_tab_id; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.alterTableConfigureTriggers (p_tab_id int4) is 368s 'alterTableConfigureTriggers (tab_id) 368s 368s Set the enable/disable configuration for the replication triggers 368s according to the origin of the set.'; 368s COMMENT 368s create or replace function public.resubscribeNode (p_origin int4, 368s p_provider int4, p_receiver int4) 368s returns bigint 368s as $$ 368s declare 368s v_record record; 368s v_missing_sets text; 368s v_ev_seqno bigint; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- 368s -- Check that the receiver exists 368s -- 368s if not exists (select no_id from public.sl_node where no_id= 368s p_receiver) then 368s raise exception 'Slony-I: subscribeSet() receiver % does not exist' , p_receiver; 368s end if; 368s 368s -- 368s -- Check that the provider exists 368s -- 368s if not exists (select no_id from public.sl_node where no_id= 368s p_provider) then 368s raise exception 'Slony-I: subscribeSet() provider % does not exist' , p_provider; 368s end if; 368s 368s 368s -- ---- 368s -- Check that this is called on the origin node 368s -- ---- 368s if p_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: subscribeSet() must be called on origin'; 368s end if; 368s 368s -- --- 368s -- Verify that the provider is either the origin or an active subscriber 368s -- Bug report #1362 368s -- --- 368s if p_origin <> p_provider then 368s for v_record in select sub1.sub_set from 368s public.sl_subscribe sub1 368s left outer join (public.sl_subscribe sub2 368s inner join 368s public.sl_set on ( 368s sl_set.set_id=sub2.sub_set 368s and sub2.sub_set=p_origin) 368s ) 368s ON ( sub1.sub_set = sub2.sub_set and 368s sub1.sub_receiver = p_provider and 368s sub1.sub_forward and sub1.sub_active 368s and sub2.sub_receiver=p_receiver) 368s 368s where sub2.sub_set is null 368s loop 368s v_missing_sets=v_missing_sets || ' ' || v_record.sub_set; 368s end loop; 368s if v_missing_sets is not null then 368s raise exception 'Slony-I: subscribeSet(): provider % is not an active forwarding node for replication set %', p_sub_provider, v_missing_sets; 368s end if; 368s end if; 368s 368s for v_record in select * from 368s public.sl_subscribe, public.sl_set where 368s sub_set=set_id and 368s sub_receiver=p_receiver 368s and set_origin=p_origin 368s loop 368s -- ---- 368s -- Create the SUBSCRIBE_SET event 368s -- ---- 368s v_ev_seqno := public.createEvent('_main', 'SUBSCRIBE_SET', 368s v_record.sub_set::text, p_provider::text, p_receiver::text, 368s case v_record.sub_forward when true then 't' else 'f' end, 368s 'f' ); 368s 368s -- ---- 368s -- Call the internal procedure to store the subscription 368s -- ---- 368s perform public.subscribeSet_int(v_record.sub_set, 368s p_provider, 368s p_receiver, v_record.sub_forward, false); 368s end loop; 368s 368s return v_ev_seqno; 368s end; 368s $$ 368s language plpgsql; 368s CREATE FUNCTION 368s create or replace function public.subscribeSet (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4, p_sub_forward bool, p_omit_copy bool) 368s returns bigint 368s as $$ 368s declare 368s v_set_origin int4; 368s v_ev_seqno int8; 368s v_ev_seqno2 int8; 368s v_rec record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- 368s -- Check that the receiver exists 368s -- 368s if not exists (select no_id from public.sl_node where no_id= 368s p_sub_receiver) then 368s raise exception 'Slony-I: subscribeSet() receiver % does not exist' , p_sub_receiver; 368s end if; 368s 368s -- 368s -- Check that the provider exists 368s -- 368s if not exists (select no_id from public.sl_node where no_id= 368s p_sub_provider) then 368s raise exception 'Slony-I: subscribeSet() provider % does not exist' , p_sub_provider; 368s end if; 368s 368s -- ---- 368s -- Check that the origin and provider of the set are remote 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_sub_set; 368s if not found then 368s raise exception 'Slony-I: subscribeSet(): set % not found', p_sub_set; 368s end if; 368s if v_set_origin = p_sub_receiver then 368s raise exception 368s 'Slony-I: subscribeSet(): set origin and receiver cannot be identical'; 368s end if; 368s if p_sub_receiver = p_sub_provider then 368s raise exception 368s 'Slony-I: subscribeSet(): set provider and receiver cannot be identical'; 368s end if; 368s -- ---- 368s -- Check that this is called on the origin node 368s -- ---- 368s if v_set_origin != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: subscribeSet() must be called on origin'; 368s end if; 368s 368s -- --- 368s -- Verify that the provider is either the origin or an active subscriber 368s -- Bug report #1362 368s -- --- 368s if v_set_origin <> p_sub_provider then 368s if not exists (select 1 from public.sl_subscribe 368s where sub_set = p_sub_set and 368s sub_receiver = p_sub_provider and 368s sub_forward and sub_active) then 368s raise exception 'Slony-I: subscribeSet(): provider % is not an active forwarding node for replication set %', p_sub_provider, p_sub_set; 368s end if; 368s end if; 368s 368s -- --- 368s -- Enforce that all sets from one origin are subscribed 368s -- using the same data provider per receiver. 368s -- ---- 368s if not exists (select 1 from public.sl_subscribe 368s where sub_set = p_sub_set and sub_receiver = p_sub_receiver) then 368s -- 368s -- New subscription - error out if we have any other subscription 368s -- from that origin with a different data provider. 368s -- 368s for v_rec in select sub_provider from public.sl_subscribe 368s join public.sl_set on set_id = sub_set 368s where set_origin = v_set_origin and sub_receiver = p_sub_receiver 368s loop 368s if v_rec.sub_provider <> p_sub_provider then 368s raise exception 'Slony-I: subscribeSet(): wrong provider % - existing subscription from origin % users provider %', 368s p_sub_provider, v_set_origin, v_rec.sub_provider; 368s end if; 368s end loop; 368s else 368s -- 368s -- Existing subscription - in case the data provider changes and 368s -- there are other subscriptions, warn here. subscribeSet_int() 368s -- will currently change the data provider for those sets as well. 368s -- 368s for v_rec in select set_id, sub_provider from public.sl_subscribe 368s join public.sl_set on set_id = sub_set 368s where set_origin = v_set_origin and sub_receiver = p_sub_receiver 368s and set_id <> p_sub_set 368s loop 368s if v_rec.sub_provider <> p_sub_provider then 368s raise exception 'Slony-I: subscribeSet(): also data provider for set % use resubscribe instead', 368s v_rec.set_id; 368s end if; 368s end loop; 368s end if; 368s 368s -- ---- 368s -- Create the SUBSCRIBE_SET event 368s -- ---- 368s v_ev_seqno := public.createEvent('_main', 'SUBSCRIBE_SET', 368s p_sub_set::text, p_sub_provider::text, p_sub_receiver::text, 368s case p_sub_forward when true then 't' else 'f' end, 368s case p_omit_copy when true then 't' else 'f' end 368s ); 368s 368s -- ---- 368s -- Call the internal procedure to store the subscription 368s -- ---- 368s v_ev_seqno2:=public.subscribeSet_int(p_sub_set, p_sub_provider, 368s p_sub_receiver, p_sub_forward, p_omit_copy); 368s 368s if v_ev_seqno2 is not null then 368s v_ev_seqno:=v_ev_seqno2; 368s end if; 368s 368s return v_ev_seqno; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.subscribeSet (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4, p_sub_forward bool, p_omit_copy bool) is 368s 'subscribeSet (sub_set, sub_provider, sub_receiver, sub_forward, omit_copy) 368s 368s Makes sure that the receiver is not the provider, then stores the 368s subscription, and publishes the SUBSCRIBE_SET event to other nodes. 368s 368s If omit_copy is true, then no data copy will be done. 368s '; 368s COMMENT 368s DROP FUNCTION IF EXISTS public.subscribeSet_int(int4,int4,int4,bool,bool); 368s DROP FUNCTION 368s create or replace function public.subscribeSet_int (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4, p_sub_forward bool, p_omit_copy bool) 368s returns int4 368s as $$ 368s declare 368s v_set_origin int4; 368s v_sub_row record; 368s v_seq_id bigint; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Lookup the set origin 368s -- ---- 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_sub_set; 368s if not found then 368s raise exception 'Slony-I: subscribeSet_int(): set % not found', p_sub_set; 368s end if; 368s 368s -- ---- 368s -- Provider change is only allowed for active sets 368s -- ---- 368s if p_sub_receiver = public.getLocalNodeId('_main') then 368s select sub_active into v_sub_row from public.sl_subscribe 368s where sub_set = p_sub_set 368s and sub_receiver = p_sub_receiver; 368s if found then 368s if not v_sub_row.sub_active then 368s raise exception 'Slony-I: subscribeSet_int(): set % is not active, cannot change provider', 368s p_sub_set; 368s end if; 368s end if; 368s end if; 368s 368s -- ---- 368s -- Try to change provider and/or forward for an existing subscription 368s -- ---- 368s update public.sl_subscribe 368s set sub_provider = p_sub_provider, 368s sub_forward = p_sub_forward 368s where sub_set = p_sub_set 368s and sub_receiver = p_sub_receiver; 368s if found then 368s 368s -- ---- 368s -- This is changing a subscriptoin. Make sure all sets from 368s -- this origin are subscribed using the same data provider. 368s -- For this we first check that the requested data provider 368s -- is subscribed to all the sets, the receiver is subscribed to. 368s -- ---- 368s for v_sub_row in select set_id from public.sl_set 368s join public.sl_subscribe on set_id = sub_set 368s where set_origin = v_set_origin 368s and sub_receiver = p_sub_receiver 368s and sub_set <> p_sub_set 368s loop 368s if not exists (select 1 from public.sl_subscribe 368s where sub_set = v_sub_row.set_id 368s and sub_receiver = p_sub_provider 368s and sub_active and sub_forward) 368s and not exists (select 1 from public.sl_set 368s where set_id = v_sub_row.set_id 368s and set_origin = p_sub_provider) 368s then 368s raise exception 'Slony-I: subscribeSet_int(): node % is not a forwarding subscriber for set %', 368s p_sub_provider, v_sub_row.set_id; 368s end if; 368s 368s -- ---- 368s -- New data provider offers this set as well, change that 368s -- subscription too. 368s -- ---- 368s update public.sl_subscribe 368s set sub_provider = p_sub_provider 368s where sub_set = v_sub_row.set_id 368s and sub_receiver = p_sub_receiver; 368s end loop; 368s 368s -- ---- 368s -- Rewrite sl_listen table 368s -- ---- 368s perform public.RebuildListenEntries(); 368s 368s return p_sub_set; 368s end if; 368s 368s -- ---- 368s -- Not found, insert a new one 368s -- ---- 368s if not exists (select true from public.sl_path 368s where pa_server = p_sub_provider 368s and pa_client = p_sub_receiver) 368s then 368s insert into public.sl_path 368s (pa_server, pa_client, pa_conninfo, pa_connretry) 368s values 368s (p_sub_provider, p_sub_receiver, 368s '', 10); 368s end if; 368s insert into public.sl_subscribe 368s (sub_set, sub_provider, sub_receiver, sub_forward, sub_active) 368s values (p_sub_set, p_sub_provider, p_sub_receiver, 368s p_sub_forward, false); 368s 368s -- ---- 368s -- If the set origin is here, then enable the subscription 368s -- ---- 368s if v_set_origin = public.getLocalNodeId('_main') then 368s select public.createEvent('_main', 'ENABLE_SUBSCRIPTION', 368s p_sub_set::text, p_sub_provider::text, p_sub_receiver::text, 368s case p_sub_forward when true then 't' else 'f' end, 368s case p_omit_copy when true then 't' else 'f' end 368s ) into v_seq_id; 368s perform public.enableSubscription(p_sub_set, 368s p_sub_provider, p_sub_receiver); 368s end if; 368s 368s -- ---- 368s -- Rewrite sl_listen table 368s -- ---- 368s perform public.RebuildListenEntries(); 368s 368s return p_sub_set; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.subscribeSet_int (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4, p_sub_forward bool, p_omit_copy bool) is 368s 'subscribeSet_int (sub_set, sub_provider, sub_receiver, sub_forward, omit_copy) 368s 368s Internal actions for subscribing receiver sub_receiver to subscription 368s set sub_set.'; 368s COMMENT 368s drop function IF EXISTS public.unsubscribeSet(int4,int4,boolean); 368s DROP FUNCTION 368s create or replace function public.unsubscribeSet (p_sub_set int4, p_sub_receiver int4,p_force boolean) 368s returns bigint 368s as $$ 368s declare 368s v_tab_row record; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- Check that this is called on the receiver node 368s -- ---- 368s if p_sub_receiver != public.getLocalNodeId('_main') then 368s raise exception 'Slony-I: unsubscribeSet() must be called on receiver'; 368s end if; 368s 368s 368s 368s -- ---- 368s -- Check that this does not break any chains 368s -- ---- 368s if p_force=false and exists (select true from public.sl_subscribe 368s where sub_set = p_sub_set 368s and sub_provider = p_sub_receiver) 368s then 368s raise exception 'Slony-I: Cannot unsubscribe set % while being provider', 368s p_sub_set; 368s end if; 368s 368s if exists (select true from public.sl_subscribe 368s where sub_set = p_sub_set 368s and sub_provider = p_sub_receiver) 368s then 368s --delete the receivers of this provider. 368s --unsubscribeSet_int() will generate the event 368s --when it runs on the receiver. 368s delete from public.sl_subscribe 368s where sub_set=p_sub_set 368s and sub_provider=p_sub_receiver; 368s end if; 368s 368s -- ---- 368s -- Remove the replication triggers. 368s -- ---- 368s for v_tab_row in select tab_id from public.sl_table 368s where tab_set = p_sub_set 368s order by tab_id 368s loop 368s perform public.alterTableDropTriggers(v_tab_row.tab_id); 368s end loop; 368s 368s -- ---- 368s -- Remove the setsync status. This will also cause the 368s -- worker thread to ignore the set and stop replicating 368s -- right now. 368s -- ---- 368s delete from public.sl_setsync 368s where ssy_setid = p_sub_set; 368s 368s -- ---- 368s -- Remove all sl_table and sl_sequence entries for this set. 368s -- Should we ever subscribe again, the initial data 368s -- copy process will create new ones. 368s -- ---- 368s delete from public.sl_table 368s where tab_set = p_sub_set; 368s delete from public.sl_sequence 368s where seq_set = p_sub_set; 368s 368s -- ---- 368s -- Call the internal procedure to drop the subscription 368s -- ---- 368s perform public.unsubscribeSet_int(p_sub_set, p_sub_receiver); 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s -- ---- 368s -- Create the UNSUBSCRIBE_SET event 368s -- ---- 368s return public.createEvent('_main', 'UNSUBSCRIBE_SET', 368s p_sub_set::text, p_sub_receiver::text); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.unsubscribeSet (p_sub_set int4, p_sub_receiver int4,force boolean) is 368s 'unsubscribeSet (sub_set, sub_receiver,force) 368s 368s Unsubscribe node sub_receiver from subscription set sub_set. This is 368s invoked on the receiver node. It verifies that this does not break 368s any chains (e.g. - where sub_receiver is a provider for another node), 368s then restores tables, drops Slony-specific keys, drops table entries 368s for the set, drops the subscription, and generates an UNSUBSCRIBE_SET 368s node to publish that the node is being dropped.'; 368s COMMENT 368s create or replace function public.unsubscribeSet_int (p_sub_set int4, p_sub_receiver int4) 368s returns int4 368s as $$ 368s declare 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- All the real work is done before event generation on the 368s -- subscriber. 368s -- ---- 368s 368s --if this event unsubscribes the provider of this node 368s --then this node should unsubscribe itself from the set as well. 368s 368s if exists (select true from 368s public.sl_subscribe where 368s sub_set=p_sub_set and sub_provider=p_sub_receiver 368s and sub_receiver=public.getLocalNodeId('_main')) 368s then 368s perform public.unsubscribeSet(p_sub_set,public.getLocalNodeId('_main'),true); 368s end if; 368s 368s 368s delete from public.sl_subscribe 368s where sub_set = p_sub_set 368s and sub_receiver = p_sub_receiver; 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return p_sub_set; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.unsubscribeSet_int (p_sub_set int4, p_sub_receiver int4) is 368s 'unsubscribeSet_int (sub_set, sub_receiver) 368s 368s All the REAL work of removing the subscriber is done before the event 368s is generated, so this function just has to drop the references to the 368s subscription in sl_subscribe.'; 368s COMMENT 368s create or replace function public.enableSubscription (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4) 368s returns int4 368s as $$ 368s begin 368s return public.enableSubscription_int (p_sub_set, 368s p_sub_provider, p_sub_receiver); 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.enableSubscription (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4) is 368s 'enableSubscription (sub_set, sub_provider, sub_receiver) 368s 368s Indicates that sub_receiver intends subscribing to set sub_set from 368s sub_provider. Work is all done by the internal function 368s enableSubscription_int (sub_set, sub_provider, sub_receiver).'; 368s COMMENT 368s create or replace function public.enableSubscription_int (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4) 368s returns int4 368s as $$ 368s declare 368s v_n int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- ---- 368s -- The real work is done in the replication engine. All 368s -- we have to do here is remembering that it happened. 368s -- ---- 368s 368s -- ---- 368s -- Well, not only ... we might be missing an important event here 368s -- ---- 368s if not exists (select true from public.sl_path 368s where pa_server = p_sub_provider 368s and pa_client = p_sub_receiver) 368s then 368s insert into public.sl_path 368s (pa_server, pa_client, pa_conninfo, pa_connretry) 368s values 368s (p_sub_provider, p_sub_receiver, 368s '', 10); 368s end if; 368s 368s update public.sl_subscribe 368s set sub_active = 't' 368s where sub_set = p_sub_set 368s and sub_receiver = p_sub_receiver; 368s get diagnostics v_n = row_count; 368s if v_n = 0 then 368s insert into public.sl_subscribe 368s (sub_set, sub_provider, sub_receiver, 368s sub_forward, sub_active) 368s values 368s (p_sub_set, p_sub_provider, p_sub_receiver, 368s false, true); 368s end if; 368s 368s -- Rewrite sl_listen table 368s perform public.RebuildListenEntries(); 368s 368s return p_sub_set; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.enableSubscription_int (p_sub_set int4, p_sub_provider int4, p_sub_receiver int4) is 368s 'enableSubscription_int (sub_set, sub_provider, sub_receiver) 368s 368s Internal function to enable subscription of node sub_receiver to set 368s sub_set via node sub_provider. 368s 368s slon does most of the work; all we need do here is to remember that it 368s happened. The function updates sl_subscribe, indicating that the 368s subscription has become active.'; 368s COMMENT 368s create or replace function public.forwardConfirm (p_con_origin int4, p_con_received int4, p_con_seqno int8, p_con_timestamp timestamp) 368s returns bigint 368s as $$ 368s declare 368s v_max_seqno bigint; 368s begin 368s select into v_max_seqno coalesce(max(con_seqno), 0) 368s from public.sl_confirm 368s where con_origin = p_con_origin 368s and con_received = p_con_received; 368s if v_max_seqno < p_con_seqno then 368s insert into public.sl_confirm 368s (con_origin, con_received, con_seqno, con_timestamp) 368s values (p_con_origin, p_con_received, p_con_seqno, 368s p_con_timestamp); 368s v_max_seqno = p_con_seqno; 368s end if; 368s 368s return v_max_seqno; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.forwardConfirm (p_con_origin int4, p_con_received int4, p_con_seqno int8, p_con_timestamp timestamp) is 368s 'forwardConfirm (p_con_origin, p_con_received, p_con_seqno, p_con_timestamp) 368s 368s Confirms (recorded in sl_confirm) that items from p_con_origin up to 368s p_con_seqno have been received by node p_con_received as of 368s p_con_timestamp, and raises an event to forward this confirmation.'; 368s COMMENT 368s create or replace function public.cleanupEvent (p_interval interval) 368s returns int4 368s as $$ 368s declare 368s v_max_row record; 368s v_min_row record; 368s v_max_sync int8; 368s v_origin int8; 368s v_seqno int8; 368s v_xmin bigint; 368s v_rc int8; 368s begin 368s -- ---- 368s -- First remove all confirmations where origin/receiver no longer exist 368s -- ---- 368s delete from public.sl_confirm 368s where con_origin not in (select no_id from public.sl_node); 368s delete from public.sl_confirm 368s where con_received not in (select no_id from public.sl_node); 368s -- ---- 368s -- Next remove all but the oldest confirm row per origin,receiver pair. 368s -- Ignore confirmations that are younger than 10 minutes. We currently 368s -- have an not confirmed suspicion that a possibly lost transaction due 368s -- to a server crash might have been visible to another session, and 368s -- that this led to log data that is needed again got removed. 368s -- ---- 368s for v_max_row in select con_origin, con_received, max(con_seqno) as con_seqno 368s from public.sl_confirm 368s where con_timestamp < (CURRENT_TIMESTAMP - p_interval) 368s group by con_origin, con_received 368s loop 368s delete from public.sl_confirm 368s where con_origin = v_max_row.con_origin 368s and con_received = v_max_row.con_received 368s and con_seqno < v_max_row.con_seqno; 368s end loop; 368s 368s -- ---- 368s -- Then remove all events that are confirmed by all nodes in the 368s -- whole cluster up to the last SYNC 368s -- ---- 368s for v_min_row in select con_origin, min(con_seqno) as con_seqno 368s from public.sl_confirm 368s group by con_origin 368s loop 368s select coalesce(max(ev_seqno), 0) into v_max_sync 368s from public.sl_event 368s where ev_origin = v_min_row.con_origin 368s and ev_seqno <= v_min_row.con_seqno 368s and ev_type = 'SYNC'; 368s if v_max_sync > 0 then 368s delete from public.sl_event 368s where ev_origin = v_min_row.con_origin 368s and ev_seqno < v_max_sync; 368s end if; 368s end loop; 368s 368s -- ---- 368s -- If cluster has only one node, then remove all events up to 368s -- the last SYNC - Bug #1538 368s -- http://gborg.postgresql.org/project/slony1/bugs/bugupdate.php?1538 368s -- ---- 368s 368s select * into v_min_row from public.sl_node where 368s no_id <> public.getLocalNodeId('_main') limit 1; 368s if not found then 368s select ev_origin, ev_seqno into v_min_row from public.sl_event 368s where ev_origin = public.getLocalNodeId('_main') 368s order by ev_origin desc, ev_seqno desc limit 1; 368s raise notice 'Slony-I: cleanupEvent(): Single node - deleting events < %', v_min_row.ev_seqno; 368s delete from public.sl_event 368s where 368s ev_origin = v_min_row.ev_origin and 368s ev_seqno < v_min_row.ev_seqno; 368s 368s end if; 368s 368s if exists (select * from "pg_catalog".pg_class c, "pg_catalog".pg_namespace n, "pg_catalog".pg_attribute a where c.relname = 'sl_seqlog' and n.oid = c.relnamespace and a.attrelid = c.oid and a.attname = 'oid') then 368s execute 'alter table public.sl_seqlog set without oids;'; 368s end if; 368s -- ---- 368s -- Also remove stale entries from the nodelock table. 368s -- ---- 368s perform public.cleanupNodelock(); 368s 368s -- ---- 368s -- Find the eldest event left, for each origin 368s -- ---- 368s for v_origin, v_seqno, v_xmin in 368s select ev_origin, ev_seqno, "pg_catalog".txid_snapshot_xmin(ev_snapshot) from public.sl_event 368s where (ev_origin, ev_seqno) in (select ev_origin, min(ev_seqno) from public.sl_event where ev_type = 'SYNC' group by ev_origin) 368s loop 368s delete from public.sl_seqlog where seql_origin = v_origin and seql_ev_seqno < v_seqno; 368s delete from public.sl_log_script where log_origin = v_origin and log_txid < v_xmin; 368s end loop; 368s 368s v_rc := public.logswitch_finish(); 368s if v_rc = 0 then -- no switch in progress 368s perform public.logswitch_start(); 368s end if; 368s 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.cleanupEvent (p_interval interval) is 368s 'cleaning old data out of sl_confirm, sl_event. Removes all but the 368s last sl_confirm row per (origin,receiver), and then removes all events 368s that are confirmed by all nodes in the whole cluster up to the last 368s SYNC.'; 368s COMMENT 368s create or replace function public.determineIdxnameUnique(p_tab_fqname text, p_idx_name name) returns name 368s as $$ 368s declare 368s v_tab_fqname_quoted text default ''; 368s v_idxrow record; 368s begin 368s v_tab_fqname_quoted := public.slon_quote_input(p_tab_fqname); 368s -- 368s -- Ensure that the table exists 368s -- 368s if (select PGC.relname 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace) is null then 368s raise exception 'Slony-I: determineIdxnameUnique(): table % not found', v_tab_fqname_quoted; 368s end if; 368s 368s -- 368s -- Lookup the tables primary key or the specified unique index 368s -- 368s if p_idx_name isnull then 368s select PGXC.relname 368s into v_idxrow 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, 368s "pg_catalog".pg_class PGXC 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace 368s and PGX.indrelid = PGC.oid 368s and PGX.indexrelid = PGXC.oid 368s and PGX.indisprimary; 368s if not found then 368s raise exception 'Slony-I: table % has no primary key', 368s v_tab_fqname_quoted; 368s end if; 368s else 368s select PGXC.relname 368s into v_idxrow 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, 368s "pg_catalog".pg_class PGXC 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace 368s and PGX.indrelid = PGC.oid 368s and PGX.indexrelid = PGXC.oid 368s and PGX.indisunique 368s and public.slon_quote_brute(PGXC.relname) = public.slon_quote_input(p_idx_name); 368s if not found then 368s raise exception 'Slony-I: table % has no unique index %', 368s v_tab_fqname_quoted, p_idx_name; 368s end if; 368s end if; 368s 368s -- 368s -- Return the found index name 368s -- 368s return v_idxrow.relname; 368s end; 368s $$ language plpgsql called on null input; 368s CREATE FUNCTION 368s comment on function public.determineIdxnameUnique(p_tab_fqname text, p_idx_name name) is 368s 'FUNCTION determineIdxnameUnique (tab_fqname, indexname) 368s 368s Given a tablename, tab_fqname, check that the unique index, indexname, 368s exists or return the primary key index name for the table. If there 368s is no unique index, it raises an exception.'; 368s COMMENT 368s create or replace function public.determineAttkindUnique(p_tab_fqname text, p_idx_name name) returns text 368s as $$ 368s declare 368s v_tab_fqname_quoted text default ''; 368s v_idx_name_quoted text; 368s v_idxrow record; 368s v_attrow record; 368s v_i integer; 368s v_attno int2; 368s v_attkind text default ''; 368s v_attfound bool; 368s begin 368s v_tab_fqname_quoted := public.slon_quote_input(p_tab_fqname); 368s v_idx_name_quoted := public.slon_quote_brute(p_idx_name); 368s -- 368s -- Ensure that the table exists 368s -- 368s if (select PGC.relname 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace) is null then 368s raise exception 'Slony-I: table % not found', v_tab_fqname_quoted; 368s end if; 368s 368s -- 368s -- Lookup the tables primary key or the specified unique index 368s -- 368s if p_idx_name isnull then 368s raise exception 'Slony-I: index name must be specified'; 368s else 368s select PGXC.relname, PGX.indexrelid, PGX.indkey 368s into v_idxrow 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_index PGX, 368s "pg_catalog".pg_class PGXC 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace 368s and PGX.indrelid = PGC.oid 368s and PGX.indexrelid = PGXC.oid 368s and PGX.indisunique 368s and public.slon_quote_brute(PGXC.relname) = v_idx_name_quoted; 368s if not found then 368s raise exception 'Slony-I: table % has no unique index %', 368s v_tab_fqname_quoted, v_idx_name_quoted; 368s end if; 368s end if; 368s 368s -- 368s -- Loop over the tables attributes and check if they are 368s -- index attributes. If so, add a "k" to the return value, 368s -- otherwise add a "v". 368s -- 368s for v_attrow in select PGA.attnum, PGA.attname 368s from "pg_catalog".pg_class PGC, 368s "pg_catalog".pg_namespace PGN, 368s "pg_catalog".pg_attribute PGA 368s where public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) = v_tab_fqname_quoted 368s and PGN.oid = PGC.relnamespace 368s and PGA.attrelid = PGC.oid 368s and not PGA.attisdropped 368s and PGA.attnum > 0 368s order by attnum 368s loop 368s v_attfound = 'f'; 368s 368s v_i := 0; 368s loop 368s select indkey[v_i] into v_attno from "pg_catalog".pg_index 368s where indexrelid = v_idxrow.indexrelid; 368s if v_attno isnull or v_attno = 0 then 368s exit; 368s end if; 368s if v_attrow.attnum = v_attno then 368s v_attfound = 't'; 368s exit; 368s end if; 368s v_i := v_i + 1; 368s end loop; 368s 368s if v_attfound then 368s v_attkind := v_attkind || 'k'; 368s else 368s v_attkind := v_attkind || 'v'; 368s end if; 368s end loop; 368s 368s -- Strip off trailing v characters as they are not needed by the logtrigger 368s v_attkind := pg_catalog.rtrim(v_attkind, 'v'); 368s 368s -- 368s -- Return the resulting attkind 368s -- 368s return v_attkind; 368s end; 368s $$ language plpgsql called on null input; 368s CREATE FUNCTION 368s comment on function public.determineAttkindUnique(p_tab_fqname text, p_idx_name name) is 368s 'determineAttKindUnique (tab_fqname, indexname) 368s 368s Given a tablename, return the Slony-I specific attkind (used for the 368s log trigger) of the table. Use the specified unique index or the 368s primary key (if indexname is NULL).'; 368s COMMENT 368s create or replace function public.RebuildListenEntries() 368s returns int 368s as $$ 368s declare 368s v_row record; 368s v_cnt integer; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s -- First remove the entire configuration 368s delete from public.sl_listen; 368s 368s -- Second populate the sl_listen configuration with a full 368s -- network of all possible paths. 368s insert into public.sl_listen 368s (li_origin, li_provider, li_receiver) 368s select pa_server, pa_server, pa_client from public.sl_path; 368s while true loop 368s insert into public.sl_listen 368s (li_origin, li_provider, li_receiver) 368s select distinct li_origin, pa_server, pa_client 368s from public.sl_listen, public.sl_path 368s where li_receiver = pa_server 368s and li_origin <> pa_client 368s and pa_conninfo<>'' 368s except 368s select li_origin, li_provider, li_receiver 368s from public.sl_listen; 368s 368s if not found then 368s exit; 368s end if; 368s end loop; 368s 368s -- We now replace specific event-origin,receiver combinations 368s -- with a configuration that tries to avoid events arriving at 368s -- a node before the data provider actually has the data ready. 368s 368s -- Loop over every possible pair of receiver and event origin 368s for v_row in select N1.no_id as receiver, N2.no_id as origin, 368s N2.no_failed as failed 368s from public.sl_node as N1, public.sl_node as N2 368s where N1.no_id <> N2.no_id 368s loop 368s -- 1st choice: 368s -- If we use the event origin as a data provider for any 368s -- set that originates on that very node, we are a direct 368s -- subscriber to that origin and listen there only. 368s if exists (select true from public.sl_set, public.sl_subscribe , public.sl_node p 368s where set_origin = v_row.origin 368s and sub_set = set_id 368s and sub_provider = v_row.origin 368s and sub_receiver = v_row.receiver 368s and sub_active 368s and p.no_active 368s and p.no_id=sub_provider 368s ) 368s then 368s delete from public.sl_listen 368s where li_origin = v_row.origin 368s and li_receiver = v_row.receiver; 368s insert into public.sl_listen (li_origin, li_provider, li_receiver) 368s values (v_row.origin, v_row.origin, v_row.receiver); 368s 368s -- 2nd choice: 368s -- If we are subscribed to any set originating on this 368s -- event origin, we want to listen on all data providers 368s -- we use for this origin. We are a cascaded subscriber 368s -- for sets from this node. 368s else 368s if exists (select true from public.sl_set, public.sl_subscribe, 368s public.sl_node provider 368s where set_origin = v_row.origin 368s and sub_set = set_id 368s and sub_provider=provider.no_id 368s and provider.no_failed = false 368s and sub_receiver = v_row.receiver 368s and sub_active) 368s then 368s delete from public.sl_listen 368s where li_origin = v_row.origin 368s and li_receiver = v_row.receiver; 368s insert into public.sl_listen (li_origin, li_provider, li_receiver) 368s select distinct set_origin, sub_provider, v_row.receiver 368s from public.sl_set, public.sl_subscribe 368s where set_origin = v_row.origin 368s and sub_set = set_id 368s and sub_receiver = v_row.receiver 368s and sub_active; 368s end if; 368s end if; 368s 368s if v_row.failed then 368s 368s --for every failed node we delete all sl_listen entries 368s --except via providers (listed in sl_subscribe) 368s --or failover candidates (sl_failover_targets) 368s --we do this to prevent a non-failover candidate 368s --that is more ahead of the failover candidate from 368s --sending events to the failover candidate that 368s --are 'too far ahead' 368s 368s --if the failed node is not an origin for any 368s --node then we don't delete all listen paths 368s --for events from it. Instead we leave 368s --the listen network alone. 368s 368s select count(*) into v_cnt from public.sl_subscribe sub, 368s public.sl_set s 368s where s.set_origin=v_row.origin and s.set_id=sub.sub_set; 368s if v_cnt > 0 then 368s delete from public.sl_listen where 368s li_origin=v_row.origin and 368s li_receiver=v_row.receiver 368s and li_provider not in 368s (select sub_provider from 368s public.sl_subscribe, 368s public.sl_set where 368s sub_set=set_id 368s and set_origin=v_row.origin); 368s end if; 368s end if; 368s -- insert into public.sl_listen 368s -- (li_origin,li_provider,li_receiver) 368s -- SELECT v_row.origin, pa_server 368s -- ,v_row.receiver 368s -- FROM public.sl_path where 368s -- pa_client=v_row.receiver 368s -- and (v_row.origin,pa_server,v_row.receiver) not in 368s -- (select li_origin,li_provider,li_receiver 368s -- from public.sl_listen); 368s -- end if; 368s end loop ; 368s 368s return null ; 368s end ; 368s $$ language 'plpgsql'; 368s CREATE FUNCTION 368s comment on function public.RebuildListenEntries() is 368s 'RebuildListenEntries() 368s 368s Invoked by various subscription and path modifying functions, this 368s rewrites the sl_listen entries, adding in all the ones required to 368s allow communications between nodes in the Slony-I cluster.'; 368s COMMENT 368s create or replace function public.generate_sync_event(p_interval interval) 368s returns int4 368s as $$ 368s declare 368s v_node_row record; 368s 368s BEGIN 368s select 1 into v_node_row from public.sl_event 368s where ev_type = 'SYNC' and ev_origin = public.getLocalNodeId('_main') 368s and ev_timestamp > now() - p_interval limit 1; 368s if not found then 368s -- If there has been no SYNC in the last interval, then push one 368s perform public.createEvent('_main', 'SYNC', NULL); 368s return 1; 368s else 368s return 0; 368s end if; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.generate_sync_event(p_interval interval) is 368s 'Generate a sync event if there has not been one in the requested interval, and this is a provider node.'; 368s COMMENT 368s drop function if exists public.updateRelname(int4, int4); 368s DROP FUNCTION 368s create or replace function public.updateRelname () 368s returns int4 368s as $$ 368s declare 368s v_no_id int4; 368s v_set_origin int4; 368s begin 368s -- ---- 368s -- Grab the central configuration lock 368s -- ---- 368s lock table public.sl_config_lock; 368s 368s update public.sl_table set 368s tab_relname = PGC.relname, tab_nspname = PGN.nspname 368s from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN 368s where public.sl_table.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid and 368s (tab_relname <> PGC.relname or tab_nspname <> PGN.nspname); 368s update public.sl_sequence set 368s seq_relname = PGC.relname, seq_nspname = PGN.nspname 368s from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN 368s where public.sl_sequence.seq_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid and 368s (seq_relname <> PGC.relname or seq_nspname <> PGN.nspname); 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.updateRelname() is 368s 'updateRelname()'; 368s COMMENT 368s drop function if exists public.updateReloid (int4, int4); 368s DROP FUNCTION 368s create or replace function public.updateReloid (p_set_id int4, p_only_on_node int4) 368s returns bigint 368s as $$ 368s declare 368s v_no_id int4; 368s v_set_origin int4; 368s prec record; 368s begin 368s -- ---- 368s -- Check that we either are the set origin or a current 368s -- subscriber of the set. 368s -- ---- 368s v_no_id := public.getLocalNodeId('_main'); 368s select set_origin into v_set_origin 368s from public.sl_set 368s where set_id = p_set_id 368s for update; 368s if not found then 368s raise exception 'Slony-I: set % not found', p_set_id; 368s end if; 368s if v_set_origin <> v_no_id 368s and not exists (select 1 from public.sl_subscribe 368s where sub_set = p_set_id 368s and sub_receiver = v_no_id) 368s then 368s return 0; 368s end if; 368s 368s -- ---- 368s -- If execution on only one node is requested, check that 368s -- we are that node. 368s -- ---- 368s if p_only_on_node > 0 and p_only_on_node <> v_no_id then 368s return 0; 368s end if; 368s 368s -- Update OIDs for tables to values pulled from non-table objects in pg_class 368s -- This ensures that we won't have collisions when repairing the oids 368s for prec in select tab_id from public.sl_table loop 368s update public.sl_table set tab_reloid = (select oid from pg_class pc where relkind <> 'r' and not exists (select 1 from public.sl_table t2 where t2.tab_reloid = pc.oid) limit 1) 368s where tab_id = prec.tab_id; 368s end loop; 368s 368s for prec in select tab_id, tab_relname, tab_nspname from public.sl_table loop 368s update public.sl_table set 368s tab_reloid = (select PGC.oid 368s from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN 368s where public.slon_quote_brute(PGC.relname) = public.slon_quote_brute(prec.tab_relname) 368s and PGC.relnamespace = PGN.oid 368s and public.slon_quote_brute(PGN.nspname) = public.slon_quote_brute(prec.tab_nspname)) 368s where tab_id = prec.tab_id; 368s end loop; 368s 368s for prec in select seq_id from public.sl_sequence loop 368s update public.sl_sequence set seq_reloid = (select oid from pg_class pc where relkind <> 'S' and not exists (select 1 from public.sl_sequence t2 where t2.seq_reloid = pc.oid) limit 1) 368s where seq_id = prec.seq_id; 368s end loop; 368s 368s for prec in select seq_id, seq_relname, seq_nspname from public.sl_sequence loop 368s update public.sl_sequence set 368s seq_reloid = (select PGC.oid 368s from pg_catalog.pg_class PGC, pg_catalog.pg_namespace PGN 368s where public.slon_quote_brute(PGC.relname) = public.slon_quote_brute(prec.seq_relname) 368s and PGC.relnamespace = PGN.oid 368s and public.slon_quote_brute(PGN.nspname) = public.slon_quote_brute(prec.seq_nspname)) 368s where seq_id = prec.seq_id; 368s end loop; 368s 368s return 1; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.updateReloid(p_set_id int4, p_only_on_node int4) is 368s 'updateReloid(set_id, only_on_node) 368s 368s Updates the respective reloids in sl_table and sl_seqeunce based on 368s their respective FQN'; 368s COMMENT 368s create or replace function public.logswitch_start() 368s returns int4 as $$ 368s DECLARE 368s v_current_status int4; 368s BEGIN 368s -- ---- 368s -- Get the current log status. 368s -- ---- 368s select last_value into v_current_status from public.sl_log_status; 368s 368s -- ---- 368s -- status = 0: sl_log_1 active, sl_log_2 clean 368s -- Initiate a switch to sl_log_2. 368s -- ---- 368s if v_current_status = 0 then 368s perform "pg_catalog".setval('public.sl_log_status', 3); 368s perform public.registry_set_timestamp( 368s 'logswitch.laststart', now()); 368s raise notice 'Slony-I: Logswitch to sl_log_2 initiated'; 368s return 2; 368s end if; 368s 368s -- ---- 368s -- status = 1: sl_log_2 active, sl_log_1 clean 368s -- Initiate a switch to sl_log_1. 368s -- ---- 368s if v_current_status = 1 then 368s perform "pg_catalog".setval('public.sl_log_status', 2); 368s perform public.registry_set_timestamp( 368s 'logswitch.laststart', now()); 368s raise notice 'Slony-I: Logswitch to sl_log_1 initiated'; 368s return 1; 368s end if; 368s 368s raise exception 'Previous logswitch still in progress'; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.logswitch_start() is 368s 'logswitch_start() 368s 368s Initiate a log table switch if none is in progress'; 368s COMMENT 368s create or replace function public.logswitch_finish() 368s returns int4 as $$ 368s DECLARE 368s v_current_status int4; 368s v_dummy record; 368s v_origin int8; 368s v_seqno int8; 368s v_xmin bigint; 368s v_purgeable boolean; 368s BEGIN 368s -- ---- 368s -- Get the current log status. 368s -- ---- 368s select last_value into v_current_status from public.sl_log_status; 368s 368s -- ---- 368s -- status value 0 or 1 means that there is no log switch in progress 368s -- ---- 368s if v_current_status = 0 or v_current_status = 1 then 368s return 0; 368s end if; 368s 368s -- ---- 368s -- status = 2: sl_log_1 active, cleanup sl_log_2 368s -- ---- 368s if v_current_status = 2 then 368s v_purgeable := 'true'; 368s 368s -- ---- 368s -- Attempt to lock sl_log_2 in order to make sure there are no other transactions 368s -- currently writing to it. Exit if it is still in use. This prevents TRUNCATE from 368s -- blocking writers to sl_log_2 while it is waiting for a lock. It also prevents it 368s -- immediately truncating log data generated inside the transaction which was active 368s -- when logswitch_finish() was called (and was blocking TRUNCATE) as soon as that 368s -- transaction is committed. 368s -- ---- 368s begin 368s lock table public.sl_log_2 in access exclusive mode nowait; 368s exception when lock_not_available then 368s raise notice 'Slony-I: could not lock sl_log_2 - sl_log_2 not truncated'; 368s return -1; 368s end; 368s 368s -- ---- 368s -- The cleanup thread calls us after it did the delete and 368s -- vacuum of both log tables. If sl_log_2 is empty now, we 368s -- can truncate it and the log switch is done. 368s -- ---- 368s for v_origin, v_seqno, v_xmin in 368s select ev_origin, ev_seqno, "pg_catalog".txid_snapshot_xmin(ev_snapshot) from public.sl_event 368s where (ev_origin, ev_seqno) in (select ev_origin, min(ev_seqno) from public.sl_event where ev_type = 'SYNC' group by ev_origin) 368s loop 368s if exists (select 1 from public.sl_log_2 where log_origin = v_origin and log_txid >= v_xmin limit 1) then 368s v_purgeable := 'false'; 368s end if; 368s end loop; 368s if not v_purgeable then 368s -- ---- 368s -- Found a row ... log switch is still in progress. 368s -- ---- 368s raise notice 'Slony-I: log switch to sl_log_1 still in progress - sl_log_2 not truncated'; 368s return -1; 368s end if; 368s 368s raise notice 'Slony-I: log switch to sl_log_1 complete - truncate sl_log_2'; 368s truncate public.sl_log_2; 368s if exists (select * from "pg_catalog".pg_class c, "pg_catalog".pg_namespace n, "pg_catalog".pg_attribute a where c.relname = 'sl_log_2' and n.oid = c.relnamespace and a.attrelid = c.oid and a.attname = 'oid') then 368s execute 'alter table public.sl_log_2 set without oids;'; 368s end if; 368s perform "pg_catalog".setval('public.sl_log_status', 0); 368s -- Run addPartialLogIndices() to try to add indices to unused sl_log_? table 368s perform public.addPartialLogIndices(); 368s 368s return 1; 368s end if; 368s 368s -- ---- 368s -- status = 3: sl_log_2 active, cleanup sl_log_1 368s -- ---- 368s if v_current_status = 3 then 368s v_purgeable := 'true'; 368s 368s -- ---- 368s -- Attempt to lock sl_log_1 in order to make sure there are no other transactions 368s -- currently writing to it. Exit if it is still in use. This prevents TRUNCATE from 368s -- blocking writes to sl_log_1 while it is waiting for a lock. It also prevents it 368s -- immediately truncating log data generated inside the transaction which was active 368s -- when logswitch_finish() was called (and was blocking TRUNCATE) as soon as that 368s -- transaction is committed. 368s -- ---- 368s begin 368s lock table public.sl_log_1 in access exclusive mode nowait; 368s exception when lock_not_available then 368s raise notice 'Slony-I: could not lock sl_log_1 - sl_log_1 not truncated'; 368s return -1; 368s end; 368s 368s -- ---- 368s -- The cleanup thread calls us after it did the delete and 368s -- vacuum of both log tables. If sl_log_2 is empty now, we 368s -- can truncate it and the log switch is done. 368s -- ---- 368s for v_origin, v_seqno, v_xmin in 368s select ev_origin, ev_seqno, "pg_catalog".txid_snapshot_xmin(ev_snapshot) from public.sl_event 368s where (ev_origin, ev_seqno) in (select ev_origin, min(ev_seqno) from public.sl_event where ev_type = 'SYNC' group by ev_origin) 368s loop 368s if (exists (select 1 from public.sl_log_1 where log_origin = v_origin and log_txid >= v_xmin limit 1)) then 368s v_purgeable := 'false'; 368s end if; 368s end loop; 368s if not v_purgeable then 368s -- ---- 368s -- Found a row ... log switch is still in progress. 368s -- ---- 368s raise notice 'Slony-I: log switch to sl_log_2 still in progress - sl_log_1 not truncated'; 368s return -1; 368s end if; 368s 368s raise notice 'Slony-I: log switch to sl_log_2 complete - truncate sl_log_1'; 368s truncate public.sl_log_1; 368s if exists (select * from "pg_catalog".pg_class c, "pg_catalog".pg_namespace n, "pg_catalog".pg_attribute a where c.relname = 'sl_log_1' and n.oid = c.relnamespace and a.attrelid = c.oid and a.attname = 'oid') then 368s execute 'alter table public.sl_log_1 set without oids;'; 368s end if; 368s perform "pg_catalog".setval('public.sl_log_status', 1); 368s -- Run addPartialLogIndices() to try to add indices to unused sl_log_? table 368s perform public.addPartialLogIndices(); 368s return 2; 368s end if; 368s END; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.logswitch_finish() is 368s 'logswitch_finish() 368s 368s Attempt to finalize a log table switch in progress 368s return values: 368s -1 if switch in progress, but not complete 368s 0 if no switch in progress 368s 1 if performed truncate on sl_log_2 368s 2 if performed truncate on sl_log_1 368s '; 368s COMMENT 368s create or replace function public.addPartialLogIndices () returns integer as $$ 368s DECLARE 368s v_current_status int4; 368s v_log int4; 368s v_dummy record; 368s v_dummy2 record; 368s idef text; 368s v_count int4; 368s v_iname text; 368s v_ilen int4; 368s v_maxlen int4; 368s BEGIN 368s v_count := 0; 368s select last_value into v_current_status from public.sl_log_status; 368s 368s -- If status is 2 or 3 --> in process of cleanup --> unsafe to create indices 368s if v_current_status in (2, 3) then 368s return 0; 368s end if; 368s 368s if v_current_status = 0 then -- Which log should get indices? 368s v_log := 2; 368s else 368s v_log := 1; 368s end if; 368s -- PartInd_test_db_sl_log_2-node-1 368s -- Add missing indices... 368s for v_dummy in select distinct set_origin from public.sl_set loop 368s v_iname := 'PartInd_main_sl_log_' || v_log::text || '-node-' 368s || v_dummy.set_origin::text; 368s -- raise notice 'Consider adding partial index % on sl_log_%', v_iname, v_log; 368s -- raise notice 'schema: [_main] tablename:[sl_log_%]', v_log; 368s select * into v_dummy2 from pg_catalog.pg_indexes where tablename = 'sl_log_' || v_log::text and indexname = v_iname; 368s if not found then 368s -- raise notice 'index was not found - add it!'; 368s v_iname := 'PartInd_main_sl_log_' || v_log::text || '-node-' || v_dummy.set_origin::text; 368s v_ilen := pg_catalog.length(v_iname); 368s v_maxlen := pg_catalog.current_setting('max_identifier_length'::text)::int4; 368s if v_ilen > v_maxlen then 368s raise exception 'Length of proposed index name [%] > max_identifier_length [%] - cluster name probably too long', v_ilen, v_maxlen; 368s end if; 368s 368s idef := 'create index "' || v_iname || 368s '" on public.sl_log_' || v_log::text || ' USING btree(log_txid) where (log_origin = ' || v_dummy.set_origin::text || ');'; 368s execute idef; 368s v_count := v_count + 1; 368s else 368s -- raise notice 'Index % already present - skipping', v_iname; 368s end if; 368s end loop; 368s 368s -- Remove unneeded indices... 368s for v_dummy in select indexname from pg_catalog.pg_indexes i where i.tablename = 'sl_log_' || v_log::text and 368s i.indexname like ('PartInd_main_sl_log_' || v_log::text || '-node-%') and 368s not exists (select 1 from public.sl_set where 368s i.indexname = 'PartInd_main_sl_log_' || v_log::text || '-node-' || set_origin::text) 368s loop 368s -- raise notice 'Dropping obsolete index %d', v_dummy.indexname; 368s idef := 'drop index public."' || v_dummy.indexname || '";'; 368s execute idef; 368s v_count := v_count - 1; 368s end loop; 368s return v_count; 368s END 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.addPartialLogIndices () is 368s 'Add partial indexes, if possible, to the unused sl_log_? table for 368s all origin nodes, and drop any that are no longer needed. 368s 368s This function presently gets run any time set origins are manipulated 368s (FAILOVER, STORE SET, MOVE SET, DROP SET), as well as each time the 368s system switches between sl_log_1 and sl_log_2.'; 368s COMMENT 368s create or replace function public.check_table_field_exists (p_namespace text, p_table text, p_field text) 368s returns bool as $$ 368s BEGIN 368s return exists ( 368s select 1 from "information_schema".columns 368s where table_schema = p_namespace 368s and table_name = p_table 368s and column_name = p_field 368s ); 368s END;$$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.check_table_field_exists (p_namespace text, p_table text, p_field text) 368s is 'Check if a table has a specific attribute'; 368s COMMENT 368s create or replace function public.add_missing_table_field (p_namespace text, p_table text, p_field text, p_type text) 368s returns bool as $$ 368s DECLARE 368s v_row record; 368s v_query text; 368s BEGIN 368s if not public.check_table_field_exists(p_namespace, p_table, p_field) then 368s raise notice 'Upgrade table %.% - add field %', p_namespace, p_table, p_field; 368s v_query := 'alter table ' || p_namespace || '.' || p_table || ' add column '; 368s v_query := v_query || p_field || ' ' || p_type || ';'; 368s execute v_query; 368s return 't'; 368s else 368s return 'f'; 368s end if; 368s END;$$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.add_missing_table_field (p_namespace text, p_table text, p_field text, p_type text) 368s is 'Add a column of a given type to a table if it is missing'; 368s COMMENT 368s create or replace function public.upgradeSchema(p_old text) 368s returns text as $$ 368s declare 368s v_tab_row record; 368s v_query text; 368s v_keepstatus text; 368s begin 368s -- If old version is pre-2.0, then we require a special upgrade process 368s if p_old like '1.%' then 368s raise exception 'Upgrading to Slony-I 2.x requires running slony_upgrade_20'; 368s end if; 368s 368s perform public.upgradeSchemaAddTruncateTriggers(); 368s 368s -- Change all Slony-I-defined columns that are "timestamp without time zone" to "timestamp *WITH* time zone" 368s if exists (select 1 from information_schema.columns c 368s where table_schema = '_main' and data_type = 'timestamp without time zone' 368s and exists (select 1 from information_schema.tables t where t.table_schema = c.table_schema and t.table_name = c.table_name and t.table_type = 'BASE TABLE') 368s and (c.table_name, c.column_name) in (('sl_confirm', 'con_timestamp'), ('sl_event', 'ev_timestamp'), ('sl_registry', 'reg_timestamp'),('sl_archive_counter', 'ac_timestamp'))) 368s then 368s 368s -- Preserve sl_status 368s select pg_get_viewdef('public.sl_status') into v_keepstatus; 368s execute 'drop view sl_status'; 368s for v_tab_row in select table_schema, table_name, column_name from information_schema.columns c 368s where table_schema = '_main' and data_type = 'timestamp without time zone' 368s and exists (select 1 from information_schema.tables t where t.table_schema = c.table_schema and t.table_name = c.table_name and t.table_type = 'BASE TABLE') 368s and (table_name, column_name) in (('sl_confirm', 'con_timestamp'), ('sl_event', 'ev_timestamp'), ('sl_registry', 'reg_timestamp'),('sl_archive_counter', 'ac_timestamp')) 368s loop 368s raise notice 'Changing Slony-I column [%.%] to timestamp WITH time zone', v_tab_row.table_name, v_tab_row.column_name; 368s v_query := 'alter table ' || public.slon_quote_brute(v_tab_row.table_schema) || 368s '.' || v_tab_row.table_name || ' alter column ' || v_tab_row.column_name || 368s ' type timestamp with time zone;'; 368s execute v_query; 368s end loop; 368s -- restore sl_status 368s execute 'create view sl_status as ' || v_keepstatus; 368s end if; 368s 368s if not exists (select 1 from information_schema.tables where table_schema = '_main' and table_name = 'sl_components') then 368s v_query := ' 368s create table public.sl_components ( 368s co_actor text not null primary key, 368s co_pid integer not null, 368s co_node integer not null, 368s co_connection_pid integer not null, 368s co_activity text, 368s co_starttime timestamptz not null, 368s co_event bigint, 368s co_eventtype text 368s ) without oids; 368s '; 368s execute v_query; 368s end if; 368s 368s 368s 368s 368s 368s if not exists (select 1 from information_schema.tables t where table_schema = '_main' and table_name = 'sl_event_lock') then 368s v_query := 'create table public.sl_event_lock (dummy integer);'; 368s execute v_query; 368s end if; 368s 368s if not exists (select 1 from information_schema.tables t 368s where table_schema = '_main' 368s and table_name = 'sl_apply_stats') then 368s v_query := ' 368s create table public.sl_apply_stats ( 368s as_origin int4, 368s as_num_insert int8, 368s as_num_update int8, 368s as_num_delete int8, 368s as_num_truncate int8, 368s as_num_script int8, 368s as_num_total int8, 368s as_duration interval, 368s as_apply_first timestamptz, 368s as_apply_last timestamptz, 368s as_cache_prepare int8, 368s as_cache_hit int8, 368s as_cache_evict int8, 368s as_cache_prepare_max int8 368s ) WITHOUT OIDS;'; 368s execute v_query; 368s end if; 368s 368s -- 368s -- On the upgrade to 2.2, we change the layout of sl_log_N by 368s -- adding columns log_tablenspname, log_tablerelname, and 368s -- log_cmdupdncols as well as changing log_cmddata into 368s -- log_cmdargs, which is a text array. 368s -- 368s if not public.check_table_field_exists('_main', 'sl_log_1', 'log_cmdargs') then 368s -- 368s -- Check that the cluster is completely caught up 368s -- 368s if public.check_unconfirmed_log() then 368s raise EXCEPTION 'cannot upgrade to new sl_log_N format due to existing unreplicated data'; 368s end if; 368s 368s -- 368s -- Drop tables sl_log_1 and sl_log_2 368s -- 368s drop table public.sl_log_1; 368s drop table public.sl_log_2; 368s 368s -- 368s -- Create the new sl_log_1 368s -- 368s create table public.sl_log_1 ( 368s log_origin int4, 368s log_txid bigint, 368s log_tableid int4, 368s log_actionseq int8, 368s log_tablenspname text, 368s log_tablerelname text, 368s log_cmdtype "char", 368s log_cmdupdncols int4, 368s log_cmdargs text[] 368s ) without oids; 368s create index sl_log_1_idx1 on public.sl_log_1 368s (log_origin, log_txid, log_actionseq); 368s 368s comment on table public.sl_log_1 is 'Stores each change to be propagated to subscriber nodes'; 368s comment on column public.sl_log_1.log_origin is 'Origin node from which the change came'; 368s comment on column public.sl_log_1.log_txid is 'Transaction ID on the origin node'; 368s comment on column public.sl_log_1.log_tableid is 'The table ID (from sl_table.tab_id) that this log entry is to affect'; 368s comment on column public.sl_log_1.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s comment on column public.sl_log_1.log_tablenspname is 'The schema name of the table affected'; 368s comment on column public.sl_log_1.log_tablerelname is 'The table name of the table affected'; 368s comment on column public.sl_log_1.log_cmdtype is 'Replication action to take. U = Update, I = Insert, D = DELETE, T = TRUNCATE'; 368s comment on column public.sl_log_1.log_cmdupdncols is 'For cmdtype=U the number of updated columns in cmdargs'; 368s comment on column public.sl_log_1.log_cmdargs is 'The data needed to perform the log action on the replica'; 368s 368s -- 368s -- Create the new sl_log_2 368s -- 368s create table public.sl_log_2 ( 368s log_origin int4, 368s log_txid bigint, 368s log_tableid int4, 368s log_actionseq int8, 368s log_tablenspname text, 368s log_tablerelname text, 368s log_cmdtype "char", 368s log_cmdupdncols int4, 368s log_cmdargs text[] 368s ) without oids; 368s create index sl_log_2_idx1 on public.sl_log_2 368s (log_origin, log_txid, log_actionseq); 368s 368s comment on table public.sl_log_2 is 'Stores each change to be propagated to subscriber nodes'; 368s comment on column public.sl_log_2.log_origin is 'Origin node from which the change came'; 368s comment on column public.sl_log_2.log_txid is 'Transaction ID on the origin node'; 368s comment on column public.sl_log_2.log_tableid is 'The table ID (from sl_table.tab_id) that this log entry is to affect'; 368s comment on column public.sl_log_2.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s comment on column public.sl_log_2.log_tablenspname is 'The schema name of the table affected'; 368s comment on column public.sl_log_2.log_tablerelname is 'The table name of the table affected'; 368s comment on column public.sl_log_2.log_cmdtype is 'Replication action to take. U = Update, I = Insert, D = DELETE, T = TRUNCATE'; 368s comment on column public.sl_log_2.log_cmdupdncols is 'For cmdtype=U the number of updated columns in cmdargs'; 368s comment on column public.sl_log_2.log_cmdargs is 'The data needed to perform the log action on the replica'; 368s 368s create table public.sl_log_script ( 368s log_origin int4, 368s log_txid bigint, 368s log_actionseq int8, 368s log_cmdtype "char", 368s log_cmdargs text[] 368s ) WITHOUT OIDS; 368s create index sl_log_script_idx1 on public.sl_log_script 368s (log_origin, log_txid, log_actionseq); 368s 368s comment on table public.sl_log_script is 'Captures SQL script queries to be propagated to subscriber nodes'; 368s comment on column public.sl_log_script.log_origin is 'Origin name from which the change came'; 368s comment on column public.sl_log_script.log_txid is 'Transaction ID on the origin node'; 368s comment on column public.sl_log_script.log_actionseq is 'The sequence number in which actions will be applied on replicas'; 368s comment on column public.sl_log_2.log_cmdtype is 'Replication action to take. S = Script statement, s = Script complete'; 368s comment on column public.sl_log_script.log_cmdargs is 'The DDL statement, optionally followed by selected nodes to execute it on.'; 368s 368s -- 368s -- Put the log apply triggers back onto sl_log_1/2 368s -- 368s create trigger apply_trigger 368s before INSERT on public.sl_log_1 368s for each row execute procedure public.logApply('_main'); 368s alter table public.sl_log_1 368s enable replica trigger apply_trigger; 368s create trigger apply_trigger 368s before INSERT on public.sl_log_2 368s for each row execute procedure public.logApply('_main'); 368s alter table public.sl_log_2 368s enable replica trigger apply_trigger; 368s end if; 368s if not exists (select 1 from information_schema.routines where routine_schema = '_main' and routine_name = 'string_agg') then 368s CREATE AGGREGATE public.string_agg(text) ( 368s SFUNC=public.agg_text_sum, 368s STYPE=text, 368s INITCOND='' 368s ); 368s end if; 368s if not exists (select 1 from information_schema.views where table_schema='_main' and table_name='sl_failover_targets') then 368s create view public.sl_failover_targets as 368s select set_id, 368s set_origin as set_origin, 368s sub1.sub_receiver as backup_id 368s 368s FROM 368s public.sl_subscribe sub1 368s ,public.sl_set set1 368s where 368s sub1.sub_set=set_id 368s and sub1.sub_forward=true 368s --exclude candidates where the set_origin 368s --has a path a node but the failover 368s --candidate has no path to that node 368s and sub1.sub_receiver not in 368s (select p1.pa_client from 368s public.sl_path p1 368s left outer join public.sl_path p2 on 368s (p2.pa_client=p1.pa_client 368s and p2.pa_server=sub1.sub_receiver) 368s where p2.pa_client is null 368s and p1.pa_server=set_origin 368s and p1.pa_client<>sub1.sub_receiver 368s ) 368s and sub1.sub_provider=set_origin 368s --exclude any subscribers that are not 368s --direct subscribers of all sets on the 368s --origin 368s and sub1.sub_receiver not in 368s (select direct_recv.sub_receiver 368s from 368s 368s (--all direct receivers of the first set 368s select subs2.sub_receiver 368s from public.sl_subscribe subs2 368s where subs2.sub_provider=set1.set_origin 368s and subs2.sub_set=set1.set_id) as 368s direct_recv 368s inner join 368s (--all other sets from the origin 368s select set_id from public.sl_set set2 368s where set2.set_origin=set1.set_origin 368s and set2.set_id<>sub1.sub_set) 368s as othersets on(true) 368s left outer join public.sl_subscribe subs3 368s on(subs3.sub_set=othersets.set_id 368s and subs3.sub_forward=true 368s and subs3.sub_provider=set1.set_origin 368s and direct_recv.sub_receiver=subs3.sub_receiver) 368s where subs3.sub_receiver is null 368s ); 368s end if; 368s 368s if not public.check_table_field_exists('_main', 'sl_node', 'no_failed') then 368s alter table public.sl_node add column no_failed bool; 368s update public.sl_node set no_failed=false; 368s end if; 368s return p_old; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s create or replace function public.check_unconfirmed_log () 368s returns bool as $$ 368s declare 368s v_rc bool = false; 368s v_error bool = false; 368s v_origin integer; 368s v_allconf bigint; 368s v_allsnap txid_snapshot; 368s v_count bigint; 368s begin 368s -- 368s -- Loop over all nodes that are the origin of at least one set 368s -- 368s for v_origin in select distinct set_origin as no_id 368s from public.sl_set loop 368s -- 368s -- Per origin determine which is the highest event seqno 368s -- that is confirmed by all subscribers to any of the 368s -- origins sets. 368s -- 368s select into v_allconf min(max_seqno) from ( 368s select con_received, max(con_seqno) as max_seqno 368s from public.sl_confirm 368s where con_origin = v_origin 368s and con_received in ( 368s select distinct sub_receiver 368s from public.sl_set as SET, 368s public.sl_subscribe as SUB 368s where SET.set_id = SUB.sub_set 368s and SET.set_origin = v_origin 368s ) 368s group by con_received 368s ) as maxconfirmed; 368s if not found then 368s raise NOTICE 'check_unconfirmed_log(): cannot determine highest ev_seqno for node % confirmed by all subscribers', v_origin; 368s v_error = true; 368s continue; 368s end if; 368s 368s -- 368s -- Get the txid snapshot that corresponds with that event 368s -- 368s select into v_allsnap ev_snapshot 368s from public.sl_event 368s where ev_origin = v_origin 368s and ev_seqno = v_allconf; 368s if not found then 368s raise NOTICE 'check_unconfirmed_log(): cannot find event %,% in sl_event', v_origin, v_allconf; 368s v_error = true; 368s continue; 368s end if; 368s 368s -- 368s -- Count the number of log rows that appeard after that event. 368s -- 368s select into v_count count(*) from ( 368s select 1 from public.sl_log_1 368s where log_origin = v_origin 368s and log_txid >= "pg_catalog".txid_snapshot_xmax(v_allsnap) 368s union all 368s select 1 from public.sl_log_1 368s where log_origin = v_origin 368s and log_txid in ( 368s select * from "pg_catalog".txid_snapshot_xip(v_allsnap) 368s ) 368s union all 368s select 1 from public.sl_log_2 368s where log_origin = v_origin 368s and log_txid >= "pg_catalog".txid_snapshot_xmax(v_allsnap) 368s union all 368s select 1 from public.sl_log_2 368s where log_origin = v_origin 368s and log_txid in ( 368s select * from "pg_catalog".txid_snapshot_xip(v_allsnap) 368s ) 368s ) as cnt; 368s 368s if v_count > 0 then 368s raise NOTICE 'check_unconfirmed_log(): origin % has % log rows that have not propagated to all subscribers yet', v_origin, v_count; 368s v_rc = true; 368s end if; 368s end loop; 368s 368s if v_error then 368s raise EXCEPTION 'check_unconfirmed_log(): aborting due to previous inconsistency'; 368s end if; 368s 368s return v_rc; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s set search_path to public 368s ; 368s SET 368s comment on function public.upgradeSchema(p_old text) is 368s 'Called during "update functions" by slonik to perform schema changes'; 368s COMMENT 368s create or replace view public.sl_status as select 368s E.ev_origin as st_origin, 368s C.con_received as st_received, 368s E.ev_seqno as st_last_event, 368s E.ev_timestamp as st_last_event_ts, 368s C.con_seqno as st_last_received, 368s C.con_timestamp as st_last_received_ts, 368s CE.ev_timestamp as st_last_received_event_ts, 368s E.ev_seqno - C.con_seqno as st_lag_num_events, 368s current_timestamp - CE.ev_timestamp as st_lag_time 368s from public.sl_event E, public.sl_confirm C, 368s public.sl_event CE 368s where E.ev_origin = C.con_origin 368s and CE.ev_origin = E.ev_origin 368s and CE.ev_seqno = C.con_seqno 368s and (E.ev_origin, E.ev_seqno) in 368s (select ev_origin, max(ev_seqno) 368s from public.sl_event 368s where ev_origin = public.getLocalNodeId('_main') 368s group by 1 368s ) 368s and (C.con_origin, C.con_received, C.con_seqno) in 368s (select con_origin, con_received, max(con_seqno) 368s from public.sl_confirm 368s where con_origin = public.getLocalNodeId('_main') 368s group by 1, 2 368s ); 368s CREATE VIEW 368s comment on view public.sl_status is 'View showing how far behind remote nodes are.'; 368s COMMENT 368s create or replace function public.copyFields(p_tab_id integer) 368s returns text 368s as $$ 368s declare 368s result text; 368s prefix text; 368s prec record; 368s begin 368s result := ''; 368s prefix := '('; -- Initially, prefix is the opening paren 368s 368s for prec in select public.slon_quote_input(a.attname) as column from public.sl_table t, pg_catalog.pg_attribute a where t.tab_id = p_tab_id and t.tab_reloid = a.attrelid and a.attnum > 0 and a.attisdropped = false order by attnum 368s loop 368s result := result || prefix || prec.column; 368s prefix := ','; -- Subsequently, prepend columns with commas 368s end loop; 368s result := result || ')'; 368s return result; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.copyFields(p_tab_id integer) is 368s 'Return a string consisting of what should be appended to a COPY statement 368s to specify fields for the passed-in tab_id. 368s 368s In PG versions > 7.3, this looks like (field1,field2,...fieldn)'; 368s COMMENT 368s create or replace function public.prepareTableForCopy(p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_tab_oid oid; 368s v_tab_fqname text; 368s begin 368s -- ---- 368s -- Get the OID and fully qualified name for the table 368s -- --- 368s select PGC.oid, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s into v_tab_oid, v_tab_fqname 368s from public.sl_table T, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where T.tab_id = p_tab_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid; 368s if not found then 368s raise exception 'Table with ID % not found in sl_table', p_tab_id; 368s end if; 368s 368s -- ---- 368s -- Try using truncate to empty the table and fallback to 368s -- delete on error. 368s -- ---- 368s perform public.TruncateOnlyTable(v_tab_fqname); 368s raise notice 'truncate of % succeeded', v_tab_fqname; 368s 368s -- suppress index activity 368s perform public.disable_indexes_on_table(v_tab_oid); 368s 368s return 1; 368s exception when others then 368s raise notice 'truncate of % failed - doing delete', v_tab_fqname; 368s perform public.disable_indexes_on_table(v_tab_oid); 368s execute 'delete from only ' || public.slon_quote_input(v_tab_fqname); 368s return 0; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.prepareTableForCopy(p_tab_id int4) is 368s 'Delete all data and suppress index maintenance'; 368s COMMENT 368s create or replace function public.finishTableAfterCopy(p_tab_id int4) 368s returns int4 368s as $$ 368s declare 368s v_tab_oid oid; 368s v_tab_fqname text; 368s begin 368s -- ---- 368s -- Get the tables OID and fully qualified name 368s -- --- 368s select PGC.oid, 368s public.slon_quote_brute(PGN.nspname) || '.' || 368s public.slon_quote_brute(PGC.relname) as tab_fqname 368s into v_tab_oid, v_tab_fqname 368s from public.sl_table T, 368s "pg_catalog".pg_class PGC, "pg_catalog".pg_namespace PGN 368s where T.tab_id = p_tab_id 368s and T.tab_reloid = PGC.oid 368s and PGC.relnamespace = PGN.oid; 368s if not found then 368s raise exception 'Table with ID % not found in sl_table', p_tab_id; 368s end if; 368s 368s -- ---- 368s -- Reenable indexes and reindex the table. 368s -- ---- 368s perform public.enable_indexes_on_table(v_tab_oid); 368s execute 'reindex table ' || public.slon_quote_input(v_tab_fqname); 368s 368s return 1; 368s end; 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.finishTableAfterCopy(p_tab_id int4) is 368s 'Reenable index maintenance and reindex the table'; 368s COMMENT 368s create or replace function public.setup_vactables_type () returns integer as $$ 368s begin 368s if not exists (select 1 from pg_catalog.pg_type t, pg_catalog.pg_namespace n 368s where n.nspname = '_main' and t.typnamespace = n.oid and 368s t.typname = 'vactables') then 368s execute 'create type public.vactables as (nspname name, relname name);'; 368s end if; 368s return 1; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.setup_vactables_type () is 368s 'Function to be run as part of loading slony1_funcs.sql that creates the vactables type if it is missing'; 368s COMMENT 368s select public.setup_vactables_type(); 368s setup_vactables_type 368s ---------------------- 368s 1 368s (1 row) 368s 368s drop function public.setup_vactables_type (); 368s DROP FUNCTION 368s create or replace function public.TablesToVacuum () returns setof public.vactables as $$ 368s declare 368s prec public.vactables%rowtype; 368s begin 368s prec.nspname := '_main'; 368s prec.relname := 'sl_event'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_confirm'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_setsync'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_seqlog'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_archive_counter'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_components'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := '_main'; 368s prec.relname := 'sl_log_script'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := 'pg_catalog'; 368s prec.relname := 'pg_listener'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s prec.nspname := 'pg_catalog'; 368s prec.relname := 'pg_statistic'; 368s if public.ShouldSlonyVacuumTable(prec.nspname, prec.relname) then 368s return next prec; 368s end if; 368s 368s return; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.TablesToVacuum () is 368s 'Return a list of tables that require frequent vacuuming. The 368s function is used so that the list is not hardcoded into C code.'; 368s COMMENT 368s create or replace function public.add_empty_table_to_replication(p_set_id int4, p_tab_id int4, p_nspname text, p_tabname text, p_idxname text, p_comment text) returns bigint as $$ 368s declare 368s 368s prec record; 368s v_origin int4; 368s v_isorigin boolean; 368s v_fqname text; 368s v_query text; 368s v_rows integer; 368s v_idxname text; 368s 368s begin 368s -- Need to validate that the set exists; the set will tell us if this is the origin 368s select set_origin into v_origin from public.sl_set where set_id = p_set_id; 368s if not found then 368s raise exception 'add_empty_table_to_replication: set % not found!', p_set_id; 368s end if; 368s 368s -- Need to be aware of whether or not this node is origin for the set 368s v_isorigin := ( v_origin = public.getLocalNodeId('_main') ); 368s 368s v_fqname := '"' || p_nspname || '"."' || p_tabname || '"'; 368s -- Take out a lock on the table 368s v_query := 'lock ' || v_fqname || ';'; 368s execute v_query; 368s 368s if v_isorigin then 368s -- On the origin, verify that the table is empty, failing if it has any tuples 368s v_query := 'select 1 as tuple from ' || v_fqname || ' limit 1;'; 368s execute v_query into prec; 368s GET DIAGNOSTICS v_rows = ROW_COUNT; 368s if v_rows = 0 then 368s raise notice 'add_empty_table_to_replication: table % empty on origin - OK', v_fqname; 368s else 368s raise exception 'add_empty_table_to_replication: table % contained tuples on origin node %', v_fqname, v_origin; 368s end if; 368s else 368s -- On other nodes, TRUNCATE the table 368s v_query := 'truncate ' || v_fqname || ';'; 368s execute v_query; 368s end if; 368s -- If p_idxname is NULL, then look up the PK index, and RAISE EXCEPTION if one does not exist 368s if p_idxname is NULL then 368s select c2.relname into prec from pg_catalog.pg_index i, pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_namespace n where i.indrelid = c1.oid and i.indexrelid = c2.oid and c1.relname = p_tabname and i.indisprimary and n.nspname = p_nspname and n.oid = c1.relnamespace; 368s if not found then 368s raise exception 'add_empty_table_to_replication: table % has no primary key and no candidate specified!', v_fqname; 368s else 368s v_idxname := prec.relname; 368s end if; 368s else 368s v_idxname := p_idxname; 368s end if; 368s return public.setAddTable_int(p_set_id, p_tab_id, v_fqname, v_idxname, p_comment); 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.add_empty_table_to_replication(p_set_id int4, p_tab_id int4, p_nspname text, p_tabname text, p_idxname text, p_comment text) is 368s 'Verify that a table is empty, and add it to replication. 368s tab_idxname is optional - if NULL, then we use the primary key. 368s 368s Note that this function is to be run within an EXECUTE SCRIPT script, 368s so it runs at the right place in the transaction stream on all 368s nodes.'; 368s COMMENT 368s create or replace function public.replicate_partition(p_tab_id int4, p_nspname text, p_tabname text, p_idxname text, p_comment text) returns bigint as $$ 368s declare 368s prec record; 368s prec2 record; 368s v_set_id int4; 368s 368s begin 368s -- Look up the parent table; fail if it does not exist 368s select c1.oid into prec from pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i, pg_catalog.pg_namespace n where c1.oid = i.inhparent and c2.oid = i.inhrelid and n.oid = c2.relnamespace and n.nspname = p_nspname and c2.relname = p_tabname; 368s if not found then 368s raise exception 'replicate_partition: No parent table found for %.%!', p_nspname, p_tabname; 368s end if; 368s 368s -- The parent table tells us what replication set to use 368s select tab_set into prec2 from public.sl_table where tab_reloid = prec.oid; 368s if not found then 368s raise exception 'replicate_partition: Parent table % for new partition %.% is not replicated!', prec.oid, p_nspname, p_tabname; 368s end if; 368s 368s v_set_id := prec2.tab_set; 368s 368s -- Now, we have all the parameters necessary to run add_empty_table_to_replication... 368s return public.add_empty_table_to_replication(v_set_id, p_tab_id, p_nspname, p_tabname, p_idxname, p_comment); 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.replicate_partition(p_tab_id int4, p_nspname text, p_tabname text, p_idxname text, p_comment text) is 368s 'Add a partition table to replication. 368s tab_idxname is optional - if NULL, then we use the primary key. 368s This function looks up replication configuration via the parent table. 368s 368s Note that this function is to be run within an EXECUTE SCRIPT script, 368s so it runs at the right place in the transaction stream on all 368s nodes.'; 368s COMMENT 368s create or replace function public.disable_indexes_on_table (i_oid oid) 368s returns integer as $$ 368s begin 368s -- Setting pg_class.relhasindex to false will cause copy not to 368s -- maintain any indexes. At the end of the copy we will reenable 368s -- them and reindex the table. This bulk creating of indexes is 368s -- faster. 368s 368s update pg_catalog.pg_class set relhasindex ='f' where oid = i_oid; 368s return 1; 368s end $$ 368s language plpgsql; 368s CREATE FUNCTION 368s comment on function public.disable_indexes_on_table(i_oid oid) is 368s 'disable indexes on the specified table. 368s Used during subscription process to suppress indexes, which allows 368s COPY to go much faster. 368s 368s This may be set as a SECURITY DEFINER in order to eliminate the need 368s for superuser access by Slony-I. 368s '; 368s COMMENT 368s create or replace function public.enable_indexes_on_table (i_oid oid) 368s returns integer as $$ 368s begin 368s update pg_catalog.pg_class set relhasindex ='t' where oid = i_oid; 368s return 1; 368s end $$ 368s language plpgsql 368s security definer; 368s CREATE FUNCTION 368s comment on function public.enable_indexes_on_table(i_oid oid) is 368s 're-enable indexes on the specified table. 368s 368s This may be set as a SECURITY DEFINER in order to eliminate the need 368s for superuser access by Slony-I. 368s '; 368s COMMENT 368s drop function if exists public.reshapeSubscription(int4,int4,int4); 368s DROP FUNCTION 368s create or replace function public.reshapeSubscription (p_sub_origin int4, p_sub_provider int4, p_sub_receiver int4) returns int4 as $$ 368s begin 368s update public.sl_subscribe 368s set sub_provider=p_sub_provider 368s from public.sl_set 368s WHERE sub_set=sl_set.set_id 368s and sl_set.set_origin=p_sub_origin and sub_receiver=p_sub_receiver; 368s if found then 368s perform public.RebuildListenEntries(); 368s notify "_main_Restart"; 368s end if; 368s return 0; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.reshapeSubscription(p_sub_origin int4, p_sub_provider int4, p_sub_receiver int4) is 368s 'Run on a receiver/subscriber node when the provider for that 368s subscription is being changed. Slonik will invoke this method 368s before the SUBSCRIBE_SET event propogates to the receiver 368s so listen paths can be updated.'; 368s COMMENT 368s NOTICE: function public.reshapesubscription(int4,int4,int4) does not exist, skipping 368s create or replace function public.slon_node_health_check() returns boolean as $$ 368s declare 368s prec record; 368s all_ok boolean; 368s begin 368s all_ok := 't'::boolean; 368s -- validate that all tables in sl_table have: 368s -- sl_table agreeing with pg_class 368s for prec in select tab_id, tab_relname, tab_nspname from 368s public.sl_table t where not exists (select 1 from pg_catalog.pg_class c, pg_catalog.pg_namespace n 368s where c.oid = t.tab_reloid and c.relname = t.tab_relname and c.relnamespace = n.oid and n.nspname = t.tab_nspname) loop 368s all_ok := 'f'::boolean; 368s raise warning 'table [id,nsp,name]=[%,%,%] - sl_table does not match pg_class/pg_namespace', prec.tab_id, prec.tab_relname, prec.tab_nspname; 368s end loop; 368s if not all_ok then 368s raise warning 'Mismatch found between sl_table and pg_class. Slonik command REPAIR CONFIG may be useful to rectify this.'; 368s end if; 368s return all_ok; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.slon_node_health_check() is 'called when slon starts up to validate that there are not problems with node configuration. Returns t if all is OK, f if there is a problem.'; 368s COMMENT 368s create or replace function public.log_truncate () returns trigger as 368s $$ 368s declare 368s r_role text; 368s c_nspname text; 368s c_relname text; 368s c_log integer; 368s c_node integer; 368s c_tabid integer; 368s begin 368s -- Ignore this call if session_replication_role = 'local' 368s select into r_role setting 368s from pg_catalog.pg_settings where name = 'session_replication_role'; 368s if r_role = 'local' then 368s return NULL; 368s end if; 368s 368s c_tabid := tg_argv[0]; 368s c_node := public.getLocalNodeId('_main'); 368s select tab_nspname, tab_relname into c_nspname, c_relname 368s from public.sl_table where tab_id = c_tabid; 368s select last_value into c_log from public.sl_log_status; 368s if c_log in (0, 2) then 368s insert into public.sl_log_1 ( 368s log_origin, log_txid, log_tableid, 368s log_actionseq, log_tablenspname, 368s log_tablerelname, log_cmdtype, 368s log_cmdupdncols, log_cmdargs 368s ) values ( 368s c_node, pg_catalog.txid_current(), c_tabid, 368s nextval('public.sl_action_seq'), c_nspname, 368s c_relname, 'T', 0, '{}'::text[]); 368s else -- (1, 3) 368s insert into public.sl_log_2 ( 368s log_origin, log_txid, log_tableid, 368s log_actionseq, log_tablenspname, 368s log_tablerelname, log_cmdtype, 368s log_cmdupdncols, log_cmdargs 368s ) values ( 368s c_node, pg_catalog.txid_current(), c_tabid, 368s nextval('public.sl_action_seq'), c_nspname, 368s c_relname, 'T', 0, '{}'::text[]); 368s end if; 368s return NULL; 368s end 368s $$ language plpgsql 368s security definer; 368s CREATE FUNCTION 368s comment on function public.log_truncate () 368s is 'trigger function run when a replicated table receives a TRUNCATE request'; 368s COMMENT 368s create or replace function public.deny_truncate () returns trigger as 368s $$ 368s declare 368s r_role text; 368s begin 368s -- Ignore this call if session_replication_role = 'local' 368s select into r_role setting 368s from pg_catalog.pg_settings where name = 'session_replication_role'; 368s if r_role = 'local' then 368s return NULL; 368s end if; 368s 368s raise exception 'truncation of replicated table forbidden on subscriber node'; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.deny_truncate () 368s is 'trigger function run when a replicated table receives a TRUNCATE request'; 368s COMMENT 368s create or replace function public.store_application_name (i_name text) returns text as $$ 368s declare 368s p_command text; 368s begin 368s if exists (select 1 from pg_catalog.pg_settings where name = 'application_name') then 368s p_command := 'set application_name to '''|| i_name || ''';'; 368s execute p_command; 368s return i_name; 368s end if; 368s return NULL::text; 368s end $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.store_application_name (i_name text) is 368s 'Set application_name GUC, if possible. Returns NULL if it fails to work.'; 368s COMMENT 368s create or replace function public.is_node_reachable(origin_node_id integer, 368s receiver_node_id integer) returns boolean as $$ 368s declare 368s listen_row record; 368s reachable boolean; 368s begin 368s reachable:=false; 368s select * into listen_row from public.sl_listen where 368s li_origin=origin_node_id and li_receiver=receiver_node_id; 368s if found then 368s reachable:=true; 368s end if; 368s return reachable; 368s end $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.is_node_reachable(origin_node_id integer, receiver_node_id integer) 368s is 'Is the receiver node reachable from the origin, via any of the listen paths?'; 368s COMMENT 368s create or replace function public.component_state (i_actor text, i_pid integer, i_node integer, i_conn_pid integer, i_activity text, i_starttime timestamptz, i_event bigint, i_eventtype text) returns integer as $$ 368s begin 368s -- Trim out old state for this component 368s if not exists (select 1 from public.sl_components where co_actor = i_actor) then 368s insert into public.sl_components 368s (co_actor, co_pid, co_node, co_connection_pid, co_activity, co_starttime, co_event, co_eventtype) 368s values 368s (i_actor, i_pid, i_node, i_conn_pid, i_activity, i_starttime, i_event, i_eventtype); 368s else 368s update public.sl_components 368s set 368s co_connection_pid = i_conn_pid, co_activity = i_activity, co_starttime = i_starttime, co_event = i_event, 368s co_eventtype = i_eventtype 368s where co_actor = i_actor 368s and co_starttime < i_starttime; 368s end if; 368s return 1; 368s end $$ 368s language plpgsql; 368s CREATE FUNCTION 368s comment on function public.component_state (i_actor text, i_pid integer, i_node integer, i_conn_pid integer, i_activity text, i_starttime timestamptz, i_event bigint, i_eventtype text) is 368s 'Store state of a Slony component. Useful for monitoring'; 368s COMMENT 368s create or replace function public.recreate_log_trigger(p_fq_table_name text, 368s p_tab_id oid, p_tab_attkind text) returns integer as $$ 368s begin 368s execute 'drop trigger "_main_logtrigger" on ' || 368s p_fq_table_name ; 368s -- ---- 368s execute 'create trigger "_main_logtrigger"' || 368s ' after insert or update or delete on ' || 368s p_fq_table_name 368s || ' for each row execute procedure public.logTrigger (' || 368s pg_catalog.quote_literal('_main') || ',' || 368s pg_catalog.quote_literal(p_tab_id::text) || ',' || 368s pg_catalog.quote_literal(p_tab_attkind) || ');'; 368s return 0; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s comment on function public.recreate_log_trigger(p_fq_table_name text, 368s p_tab_id oid, p_tab_attkind text) is 368s 'A function that drops and recreates the log trigger on the specified table. 368s It is intended to be used after the primary_key/unique index has changed.'; 368s COMMENT 368s create or replace function public.repair_log_triggers(only_locked boolean) 368s returns integer as $$ 368s declare 368s retval integer; 368s table_row record; 368s begin 368s retval=0; 368s for table_row in 368s select tab_nspname,tab_relname, 368s tab_idxname, tab_id, mode, 368s public.determineAttKindUnique(tab_nspname|| 368s '.'||tab_relname,tab_idxname) as attkind 368s from 368s public.sl_table 368s left join 368s pg_locks on (relation=tab_reloid and pid=pg_backend_pid() 368s and mode='AccessExclusiveLock') 368s ,pg_trigger 368s where tab_reloid=tgrelid and 368s public.determineAttKindUnique(tab_nspname||'.' 368s ||tab_relname,tab_idxname) 368s !=(public.decode_tgargs(tgargs))[2] 368s and tgname = '_main' 368s || '_logtrigger' 368s LOOP 368s if (only_locked=false) or table_row.mode='AccessExclusiveLock' then 368s perform public.recreate_log_trigger 368s (table_row.tab_nspname||'.'||table_row.tab_relname, 368s table_row.tab_id,table_row.attkind); 368s retval=retval+1; 368s else 368s raise notice '%.% has an invalid configuration on the log trigger. This was not corrected because only_lock is true and the table is not locked.', 368s table_row.tab_nspname,table_row.tab_relname; 368s 368s end if; 368s end loop; 368s return retval; 368s end 368s $$ 368s language plpgsql; 368s CREATE FUNCTION 368s comment on function public.repair_log_triggers(only_locked boolean) 368s is ' 368s repair the log triggers as required. If only_locked is true then only 368s tables that are already exclusively locked by the current transaction are 368s repaired. Otherwise all replicated tables with outdated trigger arguments 368s are recreated.'; 368s COMMENT 368s create or replace function public.unsubscribe_abandoned_sets(p_failed_node int4) returns bigint 368s as $$ 368s declare 368s v_row record; 368s v_seq_id bigint; 368s v_local_node int4; 368s begin 368s 368s select public.getLocalNodeId('_main') into 368s v_local_node; 368s 368s if found then 368s --abandon all subscriptions from this origin. 368s for v_row in select sub_set,sub_receiver from 368s public.sl_subscribe, public.sl_set 368s where sub_set=set_id and set_origin=p_failed_node 368s and sub_receiver=v_local_node 368s loop 368s raise notice 'Slony-I: failover_abandon_set() is abandoning subscription to set % on node % because it is too far ahead', v_row.sub_set, 368s v_local_node; 368s --If this node is a provider for the set 368s --then the receiver needs to be unsubscribed. 368s -- 368s select public.unsubscribeSet(v_row.sub_set, 368s v_local_node,true) 368s into v_seq_id; 368s end loop; 368s end if; 368s 368s return v_seq_id; 368s end 368s $$ language plpgsql; 368s CREATE FUNCTION 368s CREATE OR replace function public.agg_text_sum(txt_before TEXT, txt_new TEXT) RETURNS TEXT AS 368s $BODY$ 368s DECLARE 368s c_delim text; 368s BEGIN 368s c_delim = ','; 368s IF (txt_before IS NULL or txt_before='') THEN 368s RETURN txt_new; 368s END IF; 368s RETURN txt_before || c_delim || txt_new; 368s END; 368s $BODY$ 368s LANGUAGE plpgsql; 368s CREATE FUNCTION 368s comment on function public.agg_text_sum(text,text) is 368s 'An accumulator function used by the slony string_agg function to 368s aggregate rows into a string'; 368s COMMENT 368s Dropping cluster 16/regress ... 368s ### End 16 psql ### 368s autopkgtest [17:43:18]: test load-functions: -----------------------] 369s load-functions PASS 369s autopkgtest [17:43:19]: test load-functions: - - - - - - - - - - results - - - - - - - - - - 369s autopkgtest [17:43:19]: @@@@@@@@@@@@@@@@@@@@ summary 369s load-functions PASS 382s Creating nova instance adt-noble-arm64-slony1-2-20240311-173710-juju-7f2275-prod-proposed-migration-environment-2 from image adt/ubuntu-noble-arm64-server-20240311.img (UUID 900cfff9-7f1a-42c7-81a7-22635cd2a5f9)...