From f4ab02a48ef6abbeb0c490ad5d6ff4bdc4c88cf2 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Wed, 19 Nov 2025 14:27:50 +0100 Subject: [PATCH 1/3] Test improvement. - Arrange regression tests to improve stability. - Add a basic TAP test that holds up the lolor behaviour in case of a dynamically loaded library. --- expected/lolor.out | 2 +- sql/lolor.sql | 1 + t/001_lolor_basic.pl | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/expected/lolor.out b/expected/lolor.out index e626580..603dd93 100644 --- a/expected/lolor.out +++ b/expected/lolor.out @@ -1,11 +1,11 @@ -- Basic checks +\set VERBOSITY terse LOAD 'lolor'; SET lolor.node = 1; CREATE EXTENSION lolor; SELECT lo_creat(-1) AS loid \gset SELECT lo_from_bytea(:loid, 'Example large object'); -- ERROR ERROR: duplicate key value violates unique constraint "pg_largeobject_metadata_pkey" -DETAIL: Key (oid)=(262769) already exists. BEGIN; SELECT lo_open(:loid, x'60000'::int) AS fd \gset SELECT lowrite(:fd, 'Example large object'); diff --git a/sql/lolor.sql b/sql/lolor.sql index 2aab313..cf8a571 100644 --- a/sql/lolor.sql +++ b/sql/lolor.sql @@ -1,4 +1,5 @@ -- Basic checks +\set VERBOSITY terse LOAD 'lolor'; SET lolor.node = 1; diff --git a/t/001_lolor_basic.pl b/t/001_lolor_basic.pl index bdd1c02..8502b47 100644 --- a/t/001_lolor_basic.pl +++ b/t/001_lolor_basic.pl @@ -13,9 +13,46 @@ use Test::More; my $node = PostgreSQL::Test::Cluster->new('main'); -my $result; +my ($result, $stdout, $stderr); $node->init; + +# ############################################################################## +# +# Lolor is loaded dynamically, on demand +# +# ############################################################################## + +$node->start; +$node->safe_psql('postgres', "CREATE EXTENSION lolor"); + +# Check +($result, $stdout, $stderr) = $node->psql('postgres', qq( + SET lolor.node = 0; + SELECT lo_creat(-1) +)); +like($stderr, qr/value for lolor.node is not set/, "Zero value of lolor node is treated as an unset"); + +$result = $node->safe_psql('postgres', qq( + SET lolor.node = 1; + SELECT lo_creat(-1); +)); +ok($result > 0, "Lolor works and produces LO IDs"); + +$node->safe_psql('postgres', "DROP EXTENSION lolor"); +$result = $node->safe_psql('postgres', qq( + SET lolor.node = 0; + SELECT lo_creat(-1); +)); +ok($result > 0, "Lolor has been removed and standard lo_creat routine is used"); +$node->stop(); + +# ############################################################################## +# +# Tests when lolor is loaded statically +# +# ############################################################################## + $node->append_conf('postgresql.conf', qq{shared_preload_libraries = 'lolor'}); $node->start; From a07e5754cd3bd8b443d51f6e149deb8a62e1d1bc Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Wed, 19 Nov 2025 15:06:56 +0100 Subject: [PATCH 2/3] Fix lolor's binary upgrade. The extension substitutes LO-related routines in pg_catalog with its own versions. As a result, pg_upgrade gets stuck in a conflict between existing and extension-created database objects. Aside from replacing the 'prosrc' value in the pg_proc table, we can only introduce 'pre-' and 'post-' stages to the upgrade. By calling the 'lolor.disable()' routine, we restore the core functions to their original state. After the upgrade, we replace core functions again with lolor ones by calling lolor.enable(). The enable/disable machinery provides users with an additional benefit: they can easily switch between built-in and lolor LO storages, as demonstrated by TAP tests. Also, add TAP tests. Being here, check the extension upgrade as well. --- Makefile | 2 +- expected/lolor.out | 85 +++++++++++++++++++ lolor--1.2.1--1.2.2.sql | 178 ++++++++++++++++++++++++++++++++++++++++ lolor.control | 2 +- sql/lolor.sql | 38 +++++++++ t/002_pg_upgrade.pl | 79 ++++++++++++++++++ 6 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 lolor--1.2.1--1.2.2.sql create mode 100644 t/002_pg_upgrade.pl diff --git a/Makefile b/Makefile index 3efc056..caf9241 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ MODULE_big = lolor EXTENSION = lolor DATA = lolor--1.0.sql \ - lolor--1.0--1.2.1.sql + lolor--1.0--1.2.1.sql lolor--1.2.1--1.2.2.sql PGFILEDESC = "lolor - drop in large objects replacement for logical replication" OBJS = src/lolor.o src/lolor_fsstubs.o src/lolor_inv_api.o src/lolor_largeobject.o diff --git a/expected/lolor.out b/expected/lolor.out index 603dd93..a0e68e1 100644 --- a/expected/lolor.out +++ b/expected/lolor.out @@ -68,3 +68,88 @@ SELECT lo_close(:fd); END; DROP EXTENSION lolor; +-- Check extension upgrade +CREATE EXTENSION lolor VERSION '1.0'; +SELECT lo_creat(-1) AS loid \gset +ALTER EXTENSION lolor UPDATE TO '1.2.1'; +BEGIN; +SELECT lo_open(:loid, x'60000'::int) AS fd \gset +SELECT lowrite(:fd, 'Example large object'); + lowrite +--------- + 20 +(1 row) + +END; +ALTER EXTENSION lolor UPDATE TO '1.2.2'; +BEGIN; +SELECT lo_open(:loid, 262144) AS fd \gset +SELECT convert_from(loread(:fd, 1024), 'UTF8'); + convert_from +---------------------- + Example large object +(1 row) + +END; +-- +-- Basic checks for enable/disable routines. +-- +SELECT lolor.enable(); -- ERROR +NOTICE: lolor still not disabled + enable +-------- + f +(1 row) + +SELECT lo_from_bytea(1, 'Example large object stored in lolor LO storage'); + lo_from_bytea +--------------- + 1 +(1 row) + +SELECT lolor.disable(); + disable +--------- + t +(1 row) + +SELECT lo_open(1, 262144); -- 'not found' ERROR +ERROR: large object 1 does not exist +SELECT lo_from_bytea(2, 'Example large object stored in built-in LO storage'); + lo_from_bytea +--------------- + 2 +(1 row) + +-- We should see the object +SELECT lolor.enable(); + enable +-------- + t +(1 row) + +SELECT lo_open(2, 262144); -- 'not found' ERROR +ERROR: large object 2 does not exist +BEGIN; +SELECT lo_open(1, 262144) AS fd \gset +SELECT convert_from(loread(:fd, 1024), 'UTF8'); -- OK, see the object + convert_from +------------------------------------------------- + Example large object stored in lolor LO storage +(1 row) + +END; +-- To be sure that the behaviour is repeatable +SELECT lolor.disable(); + disable +--------- + t +(1 row) + +SELECT lolor.enable(); + enable +-------- + t +(1 row) + +DROP EXTENSION lolor; diff --git a/lolor--1.2.1--1.2.2.sql b/lolor--1.2.1--1.2.2.sql new file mode 100644 index 0000000..45913ca --- /dev/null +++ b/lolor--1.2.1--1.2.2.sql @@ -0,0 +1,178 @@ +/* lolor--1.2.1--1.2.2.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION lolor" to load this file. \quit + +/* + * Disable lolor functionality. + * + * Renames LO-related routines referencing lolor C-functions to corresponding + * pg_catalog.lolor_* ones. Afterwards, renames pg_catalog.*_orig routines to + * corresponding LO ones. If the lolor is disabled, do nothing. + * Designed to not create / drop any database objects. + * + * Returns true on success, false on a handled error and an ERROR otherwise. + */ +CREATE FUNCTION lolor.disable() +RETURNS boolean AS $$ +BEGIN + -- Doesn't protect a lot but provides a user with meaningful peace of information + IF NOT EXISTS (SELECT 1 FROM pg_proc where proname = 'lo_close_orig') THEN + raise NOTICE 'lolor still not enabled'; + RETURN false; + END IF; + IF EXISTS (SELECT 1 FROM pg_proc where proname = 'lolor_lo_open') THEN + raise NOTICE 'lolor.dsable() has been called before'; + RETURN false; + END IF; + + ALTER FUNCTION pg_catalog.lo_open(oid, int4) RENAME TO lolor_lo_open; + ALTER FUNCTION pg_catalog.lo_open_orig(oid, int4) RENAME TO lo_open; + + ALTER FUNCTION pg_catalog.lo_close(int4) RENAME TO lolor_lo_close; + ALTER FUNCTION pg_catalog.lo_close_orig(int4) RENAME TO lo_close; + + ALTER FUNCTION pg_catalog.lo_creat(integer) RENAME TO lolor_lo_creat; + ALTER FUNCTION pg_catalog.lo_creat_orig(integer) RENAME TO lo_creat; + + ALTER FUNCTION pg_catalog.lo_create(oid) RENAME TO lolor_lo_create; + ALTER FUNCTION pg_catalog.lo_create_orig(oid) RENAME TO lo_create; + + ALTER FUNCTION pg_catalog.loread(integer, integer) RENAME TO lolor_loread; + ALTER FUNCTION pg_catalog.loread_orig(integer, integer) RENAME TO loread; + + ALTER FUNCTION pg_catalog.lowrite(integer, bytea) RENAME TO lolor_lowrite; + ALTER FUNCTION pg_catalog.lowrite_orig(integer, bytea) RENAME TO lowrite; + + ALTER FUNCTION pg_catalog.lo_export(oid, text) RENAME TO lolor_lo_export; + ALTER FUNCTION pg_catalog.lo_export_orig(oid, text) RENAME TO lo_export; + + ALTER FUNCTION pg_catalog.lo_from_bytea(oid, bytea) RENAME TO lolor_lo_from_bytea; + ALTER FUNCTION pg_catalog.lo_from_bytea_orig(oid, bytea) RENAME TO lo_from_bytea; + + ALTER FUNCTION pg_catalog.lo_get(oid) RENAME TO lolor_lo_get; + ALTER FUNCTION pg_catalog.lo_get_orig(oid) RENAME TO lo_get; + + ALTER FUNCTION pg_catalog.lo_get(oid, bigint, integer) RENAME TO lolor_lo_get; + ALTER FUNCTION pg_catalog.lo_get_orig(oid, bigint, integer) RENAME TO lo_get; + + ALTER FUNCTION pg_catalog.lo_import(text) RENAME TO lolor_lo_import; + ALTER FUNCTION pg_catalog.lo_import_orig(text) RENAME TO lo_import; + + ALTER FUNCTION pg_catalog.lo_import(text, oid) RENAME TO lolor_lo_import; + ALTER FUNCTION pg_catalog.lo_import_orig(text, oid) RENAME TO lo_import; + + ALTER FUNCTION pg_catalog.lo_lseek(integer, integer, integer) RENAME TO lolor_lo_lseek; + ALTER FUNCTION pg_catalog.lo_lseek_orig(integer, integer, integer) RENAME TO lo_lseek; + + ALTER FUNCTION pg_catalog.lo_lseek64(integer, bigint, integer) RENAME TO lolor_lo_lseek64; + ALTER FUNCTION pg_catalog.lo_lseek64_orig(integer, bigint, integer) RENAME TO lo_lseek64; + + ALTER FUNCTION pg_catalog.lo_put(oid, bigint, bytea) RENAME TO lolor_lo_put; + ALTER FUNCTION pg_catalog.lo_put_orig(oid, bigint, bytea) RENAME TO lo_put; + + ALTER FUNCTION pg_catalog.lo_tell(integer) RENAME TO lolor_lo_tell; + ALTER FUNCTION pg_catalog.lo_tell_orig(integer) RENAME TO lo_tell; + + ALTER FUNCTION pg_catalog.lo_tell64(integer) RENAME TO lolor_lo_tell64; + ALTER FUNCTION pg_catalog.lo_tell64_orig(integer) RENAME TO lo_tell64; + + ALTER FUNCTION pg_catalog.lo_truncate(integer, integer) RENAME TO lolor_lo_truncate; + ALTER FUNCTION pg_catalog.lo_truncate_orig(integer, integer) RENAME TO lo_truncate; + + ALTER FUNCTION pg_catalog.lo_truncate64(integer, bigint) RENAME TO lolor_lo_truncate64; + ALTER FUNCTION pg_catalog.lo_truncate64_orig(integer, bigint) RENAME TO lo_truncate64; + + ALTER FUNCTION pg_catalog.lo_unlink(oid) RENAME TO lolor_lo_unlink; + ALTER FUNCTION pg_catalog.lo_unlink_orig(oid) RENAME TO lo_unlink; + + RETURN true; +END; +$$ LANGUAGE plpgsql STRICT VOLATILE; + +/* + * Enable lolor functionality, disabled by the lolor.disable() call. + * + * Renames LO-related routines to corresponding pg_catalog.*_orig. Afterwards, + * renames pg_catalog.lolor_* routines to corresponding LO ones. If the lolor is + * enabled, do nothing. + * Designed to not create / drop any database objects. + * + * Returns true on success, false on a handled error and an ERROR otherwise. + */ +CREATE FUNCTION lolor.enable() +RETURNS boolean AS $$ +BEGIN + -- Doesn't protect a lot but provides a user with meaningful peace of information + IF NOT EXISTS (SELECT 1 FROM pg_proc where proname = 'lolor_lo_open') THEN + raise NOTICE 'lolor still not disabled'; + RETURN false; + END IF; + IF EXISTS (SELECT 1 FROM pg_proc where proname = 'lo_close_orig') THEN + raise NOTICE 'lolor.enable() has been called before'; + RETURN false; + END IF; + + ALTER FUNCTION pg_catalog.lo_open(oid, int4) RENAME TO lo_open_orig; + ALTER FUNCTION pg_catalog.lolor_lo_open(oid, int4) RENAME TO lo_open; + + ALTER FUNCTION pg_catalog.lo_close(int4) RENAME TO lo_close_orig; + ALTER FUNCTION pg_catalog.lolor_lo_close(int4) RENAME TO lo_close; + + ALTER FUNCTION pg_catalog.lo_creat(integer) RENAME TO lo_creat_orig; + ALTER FUNCTION pg_catalog.lolor_lo_creat(integer) RENAME TO lo_creat; + + ALTER FUNCTION pg_catalog.lo_create(oid) RENAME TO lo_create_orig; + ALTER FUNCTION pg_catalog.lolor_lo_create(oid) RENAME TO lo_create; + + ALTER FUNCTION pg_catalog.loread(integer, integer) RENAME TO loread_orig; + ALTER FUNCTION pg_catalog.lolor_loread(integer, integer) RENAME TO loread; + + ALTER FUNCTION pg_catalog.lowrite(integer, bytea) RENAME TO lowrite_orig; + ALTER FUNCTION pg_catalog.lolor_lowrite(integer, bytea) RENAME TO lowrite; + + ALTER FUNCTION pg_catalog.lo_export(oid, text) RENAME TO lo_export_orig; + ALTER FUNCTION pg_catalog.lolor_lo_export(oid, text) RENAME TO lo_export; + + ALTER FUNCTION pg_catalog.lo_from_bytea(oid, bytea) RENAME TO lo_from_bytea_orig; + ALTER FUNCTION pg_catalog.lolor_lo_from_bytea(oid, bytea) RENAME TO lo_from_bytea; + + ALTER FUNCTION pg_catalog.lo_get(oid) RENAME TO lo_get_orig; + ALTER FUNCTION pg_catalog.lolor_lo_get(oid) RENAME TO lo_get; + + ALTER FUNCTION pg_catalog.lo_get(oid, bigint, integer) RENAME TO lo_get_orig; + ALTER FUNCTION pg_catalog.lolor_lo_get(oid, bigint, integer) RENAME TO lo_get; + + ALTER FUNCTION pg_catalog.lo_import(text) RENAME TO lo_import_orig; + ALTER FUNCTION pg_catalog.lolor_lo_import(text) RENAME TO lo_import; + + ALTER FUNCTION pg_catalog.lo_import(text, oid) RENAME TO lo_import_orig; + ALTER FUNCTION pg_catalog.lolor_lo_import(text, oid) RENAME TO lo_import; + + ALTER FUNCTION pg_catalog.lo_lseek(integer, integer, integer) RENAME TO lo_lseek_orig; + ALTER FUNCTION pg_catalog.lolor_lo_lseek(integer, integer, integer) RENAME TO lo_lseek; + + ALTER FUNCTION pg_catalog.lo_lseek64(integer, bigint, integer) RENAME TO lo_lseek64_orig; + ALTER FUNCTION pg_catalog.lolor_lo_lseek64(integer, bigint, integer) RENAME TO lo_lseek64; + + ALTER FUNCTION pg_catalog.lo_put(oid, bigint, bytea) RENAME TO lo_put_orig; + ALTER FUNCTION pg_catalog.lolor_lo_put(oid, bigint, bytea) RENAME TO lo_put; + + ALTER FUNCTION pg_catalog.lo_tell(integer) RENAME TO lo_tell_orig; + ALTER FUNCTION pg_catalog.lolor_lo_tell(integer) RENAME TO lo_tell; + + ALTER FUNCTION pg_catalog.lo_tell64(integer) RENAME TO lo_tell64_orig; + ALTER FUNCTION pg_catalog.lolor_lo_tell64(integer) RENAME TO lo_tell64; + + ALTER FUNCTION pg_catalog.lo_truncate(integer, integer) RENAME TO lo_truncate_orig; + ALTER FUNCTION pg_catalog.lolor_lo_truncate(integer, integer) RENAME TO lo_truncate; + + ALTER FUNCTION pg_catalog.lo_truncate64(integer, bigint) RENAME TO lo_truncate64_orig; + ALTER FUNCTION pg_catalog.lolor_lo_truncate64(integer, bigint) RENAME TO lo_truncate64; + + ALTER FUNCTION pg_catalog.lo_unlink(oid) RENAME TO lo_unlink_orig; + ALTER FUNCTION pg_catalog.lolor_lo_unlink(oid) RENAME TO lo_unlink; + + RETURN true; +END; +$$ LANGUAGE plpgsql STRICT VOLATILE; diff --git a/lolor.control b/lolor.control index dc528b8..e8a0385 100644 --- a/lolor.control +++ b/lolor.control @@ -1,6 +1,6 @@ # lolor extension comment = 'Large Objects support for logical replication' -default_version = '1.2.1' +default_version = '1.2.2' module_pathname = '$libdir/lolor' relocatable = false trusted = true diff --git a/sql/lolor.sql b/sql/lolor.sql index cf8a571..098b0bf 100644 --- a/sql/lolor.sql +++ b/sql/lolor.sql @@ -34,3 +34,41 @@ SELECT lo_close(:fd); END; DROP EXTENSION lolor; + +-- Check extension upgrade +CREATE EXTENSION lolor VERSION '1.0'; +SELECT lo_creat(-1) AS loid \gset +ALTER EXTENSION lolor UPDATE TO '1.2.1'; +BEGIN; +SELECT lo_open(:loid, x'60000'::int) AS fd \gset +SELECT lowrite(:fd, 'Example large object'); +END; +ALTER EXTENSION lolor UPDATE TO '1.2.2'; +BEGIN; +SELECT lo_open(:loid, 262144) AS fd \gset +SELECT convert_from(loread(:fd, 1024), 'UTF8'); +END; + +-- +-- Basic checks for enable/disable routines. +-- + +SELECT lolor.enable(); -- ERROR +SELECT lo_from_bytea(1, 'Example large object stored in lolor LO storage'); +SELECT lolor.disable(); +SELECT lo_open(1, 262144); -- 'not found' ERROR +SELECT lo_from_bytea(2, 'Example large object stored in built-in LO storage'); + +-- We should see the object +SELECT lolor.enable(); +SELECT lo_open(2, 262144); -- 'not found' ERROR +BEGIN; +SELECT lo_open(1, 262144) AS fd \gset +SELECT convert_from(loread(:fd, 1024), 'UTF8'); -- OK, see the object +END; + +-- To be sure that the behaviour is repeatable +SELECT lolor.disable(); +SELECT lolor.enable(); + +DROP EXTENSION lolor; diff --git a/t/002_pg_upgrade.pl b/t/002_pg_upgrade.pl new file mode 100644 index 0000000..b2cda08 --- /dev/null +++ b/t/002_pg_upgrade.pl @@ -0,0 +1,79 @@ +# Check binary upgrade +# +# Copyright (c) 2022-2025, pgEdge, Inc. +# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $old = PostgreSQL::Test::Cluster->new('old_node'); +my $new = PostgreSQL::Test::Cluster->new('new_node'); +my $newbindir = $new->config_data('--bindir'); +my $oldbindir = $old->config_data('--bindir'); +my ($result, $stdout, $stderr); + +# Prepare old node to be upgraded +$old->init; +$old->append_conf('postgresql.conf', qq{lolor.node = 1}); +$old->start; +$old->safe_psql('postgres', "CREATE EXTENSION lolor"); +$old->safe_psql('postgres', + qq(SELECT lo_from_bytea(1, 'lolor LO object - 1'))); +$old->safe_psql('postgres', qq( + SET lolor.node = 1; + SELECT lo_creat(-1); +)); +$old->safe_psql('postgres', "SELECT lolor.disable()"); +$old->safe_psql('postgres', + qq(SELECT lo_from_bytea(1, 'built-in LO object - 1'))); +$old->stop(); + +$new->init; +$new->append_conf('postgresql.conf', qq{lolor.node = 1}); + +command_ok( + [ + 'pg_upgrade', + '--old-datadir' => $old->data_dir, + '--new-datadir' => $new->data_dir, + '--old-bindir' => $oldbindir, + '--new-bindir' => $newbindir, + '--link', + ], + 'run of pg_upgrade for new instance'); + +$new->start; +$new->safe_psql('postgres', "SELECT 1"); + +# Should not conflict with lolor +$new->safe_psql('postgres', "SELECT lo_from_bytea(2, 'built-in LO object - 2')"); +# Should see built-in object, created on the old node +$result = $new->safe_psql('postgres', qq( + BEGIN; -- built-in object + SELECT lo_open(1, 262144) AS fd \\gset + SELECT convert_from(loread(:fd, 1024), 'UTF8'); + END; +)); +ok($result eq 'built-in LO object - 1', "Check built-in LO works after upgrade"); + +$new->safe_psql('postgres', "SELECT lolor.enable()"); +# Should not conflict with built-in LO storage +$new->safe_psql('postgres', "SELECT lo_from_bytea(2, 'lolor LO object')"); +# Should see lolor object, created on the old node +$result = $new->safe_psql('postgres', qq( + BEGIN; -- lolor object + SELECT lo_open(1, 262144) AS fd \\gset + SELECT convert_from(loread(:fd, 1024), 'UTF8'); + END; +)); +ok($result eq 'lolor LO object - 1', "Check lolor works after upgrade"); + +$new->stop(); + +done_testing(); From 7205b83f4834babf74777d9aa7152f74765e7e34 Mon Sep 17 00:00:00 2001 From: "Andrei V. Lepikhov" Date: Thu, 20 Nov 2025 10:41:13 +0100 Subject: [PATCH 3/3] Replace some oldish logic employing enable/disable routines. On the extension DROP command, call the disable() routine instead of multiple SPI calls. Also, introduce the lolor.is_enabled UI function to ensure the user can identify the current state. --- expected/lolor.out | 22 +++++++++ lolor--1.2.1--1.2.2.sql | 26 +++++++++- sql/lolor.sql | 9 ++++ src/lolor.c | 105 ++-------------------------------------- 4 files changed, 59 insertions(+), 103 deletions(-) diff --git a/expected/lolor.out b/expected/lolor.out index a0e68e1..1409abe 100644 --- a/expected/lolor.out +++ b/expected/lolor.out @@ -152,4 +152,26 @@ SELECT lolor.enable(); t (1 row) +-- Check that no tails existing after the extension drop in both enabled and +-- disabled states. DROP EXTENSION lolor; +SELECT oid, proname FROM pg_proc WHERE proname IN ('lo_open_orig', + 'lolor_lo_open'); + oid | proname +-----+--------- +(0 rows) + +CREATE EXTENSION lolor; +SELECT lolor.disable(); + disable +--------- + t +(1 row) + +DROP EXTENSION lolor; +SELECT oid, proname FROM pg_proc WHERE proname IN ('lo_open_orig', + 'lolor_lo_open'); + oid | proname +-----+--------- +(0 rows) + diff --git a/lolor--1.2.1--1.2.2.sql b/lolor--1.2.1--1.2.2.sql index 45913ca..9c62315 100644 --- a/lolor--1.2.1--1.2.2.sql +++ b/lolor--1.2.1--1.2.2.sql @@ -22,7 +22,7 @@ BEGIN RETURN false; END IF; IF EXISTS (SELECT 1 FROM pg_proc where proname = 'lolor_lo_open') THEN - raise NOTICE 'lolor.dsable() has been called before'; + raise NOTICE 'lolor.disable() has been called before'; RETURN false; END IF; @@ -176,3 +176,27 @@ BEGIN RETURN true; END; $$ LANGUAGE plpgsql STRICT VOLATILE; + +-- +-- Check if lolor functions replaces core LO routines at the moment. +-- +CREATE FUNCTION lolor.is_enabled() +RETURNS boolean AS $$ +BEGIN + -- Doesn't protect a lot but provides a user with meaningful peace of information + IF NOT EXISTS (SELECT 1 FROM pg_proc + WHERE proname IN ('lolor_lo_open', 'lo_open_orig')) THEN + raise EXCEPTION 'lolor is in inconsistent state'; + ELSIF EXISTS (SELECT 1 WHERE + EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'lolor_lo_open') AND + EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'lo_open_orig')) THEN + raise EXCEPTION 'lolor is in inconsistent state'; + END IF; + + IF EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'lolor_lo_open') THEN + RETURN false; + END IF; + + RETURN true; +END; +$$ LANGUAGE plpgsql STRICT STABLE; diff --git a/sql/lolor.sql b/sql/lolor.sql index 098b0bf..78546ee 100644 --- a/sql/lolor.sql +++ b/sql/lolor.sql @@ -71,4 +71,13 @@ END; SELECT lolor.disable(); SELECT lolor.enable(); +-- Check that no tails existing after the extension drop in both enabled and +-- disabled states. DROP EXTENSION lolor; +SELECT oid, proname FROM pg_proc WHERE proname IN ('lo_open_orig', + 'lolor_lo_open'); +CREATE EXTENSION lolor; +SELECT lolor.disable(); +DROP EXTENSION lolor; +SELECT oid, proname FROM pg_proc WHERE proname IN ('lo_open_orig', + 'lolor_lo_open'); diff --git a/src/lolor.c b/src/lolor.c index 42131a9..41df671 100644 --- a/src/lolor.c +++ b/src/lolor.c @@ -188,7 +188,7 @@ _PG_init(void) * * We cannot drop our own functions here as the dependencies of * the extension itself won't allow that. Likewise we cannot - * drop the origial PostgreSQL functions because the PostgreSQL + * drop the original PostgreSQL functions because the PostgreSQL * system depends on them. But we can get around that with * renaming (which makes no sense). */ @@ -247,107 +247,8 @@ lolor_on_drop_extension(PG_FUNCTION_ARGS) */ SPI_connect(); - SPI_execute("ALTER FUNCTION pg_catalog.lo_open(oid, int4)" - " RENAME TO lo_open_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_open_orig(oid, int4)" - " RENAME TO lo_open", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_close(int4)" - " RENAME TO lo_close_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_close_orig(int4)" - " RENAME TO lo_close", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_creat(integer)" - " RENAME TO lo_creat_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_creat_orig(integer)" - " RENAME TO lo_creat", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_create(oid)" - " RENAME TO lo_create_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_create_orig(oid)" - " RENAME TO lo_create", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.loread(integer, integer)" - " RENAME TO loread_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.loread_orig(integer, integer)" - " RENAME TO loread", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lowrite(integer, bytea)" - " RENAME TO lowrite_to_drop", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lowrite_orig(integer, bytea)" - " RENAME TO lowrite", false, 0); - - - SPI_execute("ALTER FUNCTION pg_catalog.lo_export(oid, text)" - " RENAME TO lo_export_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_export_orig(oid, text)" - " RENAME TO lo_export;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_from_bytea(oid, bytea)" - " RENAME TO lo_from_bytea_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_from_bytea_orig(oid, bytea)" - " RENAME TO lo_from_bytea;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_get(oid)" - " RENAME TO lo_get_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_get_orig(oid)" - " RENAME TO lo_get;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_get(oid, bigint, integer)" - " RENAME TO lo_get_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_get_orig(oid, bigint, integer)" - " RENAME TO lo_get;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_import(text)" - " RENAME TO lo_import_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_import_orig(text)" - " RENAME TO lo_import;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_import(text, oid)" - " RENAME TO lo_import_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_import_orig(text, oid)" - " RENAME TO lo_import;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_lseek(integer, integer, integer)" - " RENAME TO lo_lseek_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_lseek_orig(integer, integer, integer)" - " RENAME TO lo_lseek;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_lseek64(integer, bigint, integer)" - " RENAME TO lo_lseek64_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_lseek64_orig(integer, bigint, integer)" - " RENAME TO lo_lseek64;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_put(oid, bigint, bytea)" - " RENAME TO lo_put_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_put_orig(oid, bigint, bytea)" - " RENAME TO lo_put;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_tell(integer)" - " RENAME TO lo_tell_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_tell_orig(integer)" - " RENAME TO lo_tell;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_tell64(integer)" - " RENAME TO lo_tell64_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_tell64_orig(integer)" - " RENAME TO lo_tell64;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_truncate(integer, integer)" - " RENAME TO lo_truncate_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_truncate_orig(integer, integer)" - " RENAME TO lo_truncate;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_truncate64(integer, bigint)" - " RENAME TO lo_truncate64_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_truncate64_orig(integer, bigint)" - " RENAME TO lo_truncate64;", false, 0); - - SPI_execute("ALTER FUNCTION pg_catalog.lo_unlink(oid)" - " RENAME TO lo_unlink_to_drop;", false, 0); - SPI_execute("ALTER FUNCTION pg_catalog.lo_unlink_orig(oid)" - " RENAME TO lo_unlink;", false, 0); - + SPI_execute("SELECT CASE WHEN lolor.is_enabled() THEN lolor.disable() ELSE 'true' END CASE", + false, 0); SPI_finish(); PG_RETURN_NULL();