File: /volume1/@appstore/SynologyPhotos/etc/sql/119.sql
ALTER TABLE public.similar_hash
DROP CONSTRAINT IF EXISTS similar_hash_unit_item_fk,
DROP CONSTRAINT IF EXISTS similar_hash_unit_major_fk,
ADD CONSTRAINT similar_hash_unit_item_fk FOREIGN KEY (id_unit, id_item) REFERENCES public.unit (id, id_item) ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT similar_hash_unit_major_fk FOREIGN KEY (id_unit, is_major) REFERENCES public.unit (id, is_major) ON UPDATE CASCADE ON DELETE CASCADE;
DO $$
DECLARE
max_version BIGINT;
id_user INTEGER;
id_user_bucket INTEGER;
version_time_table text := 'public.version_time';
new_version_time_table text;
new_version_time_table_name text;
pk_name text;
idx_name text;
new_sequence_name text;
new_last_sequence BIGINT;
has_migrated_version_table boolean;
migrated_version_table text;
migrated_version_table_name text;
BEGIN
FOR id_user IN (SELECT id FROM public.user_info ORDER BY id) LOOP
id_user_bucket := CASE WHEN id_user = 0 THEN 0 ELSE (id_user - 1) % 13 + 1 END;
new_version_time_table = 'public.version_time_' || id_user_bucket;
new_version_time_table_name = 'version_time_' || id_user_bucket;
migrated_version_table = 'public.version_time_' || id_user;
migrated_version_table_name = 'version_time_' || id_user;
has_migrated_version_table := EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = migrated_version_table_name);
pk_name = 'version_id_' || id_user_bucket || '_primary';
idx_name = 'version_time_' || id_user_bucket || '_idx';
new_sequence_name = 'public.version_time_' || id_user_bucket || '_version_seq';
-- version_time_[id_user] created, but user has not migrated yet, thus version_time_[id_user] table may be empty or not yet copied
IF has_migrated_version_table THEN
EXECUTE format(
'SELECT COALESCE(MAX(version), 0) FROM (SELECT version FROM public.version_time UNION ALL SELECT version FROM %s) AS combined_versions',
migrated_version_table
)
INTO max_version;
-- user has not created version_time_[id_user]
ELSE
SELECT COALESCE(MAX(version), 0)
INTO max_version
FROM public.version_time;
END IF;
IF EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = new_version_time_table_name
) THEN
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s', new_version_time_table, pk_name);
-- Check if index exists before creating it
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE indexname = idx_name
AND tablename = new_version_time_table_name
) THEN
EXECUTE format('CREATE UNIQUE INDEX %s ON %s USING btree (version, modified_time ASC NULLS LAST)', idx_name, new_version_time_table);
END IF;
-- if new_version_time_seq exists and new_last_sequence is greater than max_version, then do nothing
EXECUTE format('SELECT last_value FROM %s', new_sequence_name) INTO new_last_sequence;
IF new_last_sequence >= max_version THEN
RAISE NOTICE 'Table % already exists and has version greater than %, skipping', new_version_time_table, max_version;
CONTINUE;
ELSE
-- If the new table exists but has a lower max version, update the sequence
EXECUTE format('ALTER SEQUENCE %s RESTART WITH %s', new_sequence_name, max_version + 1);
RAISE NOTICE 'Table % exists but has lower version (%), updating sequence to start with %', new_version_time_table, new_last_sequence, max_version;
END IF;
ELSE
-- remove sequence, table if exists before create new, index and trigger will be removed automatically by dropping table
EXECUTE 'DROP TABLE IF EXISTS ' || new_version_time_table || ' CASCADE';
EXECUTE 'DROP SEQUENCE IF EXISTS ' || new_sequence_name || ' CASCADE';
-- create new sequence, table, index, trigger
EXECUTE format('CREATE SEQUENCE %s START WITH %s', new_sequence_name, max_version + 1);
EXECUTE format('CREATE TABLE %s (version BIGINT NOT NULL DEFAULT nextval(''%s''), modified_time BIGINT NOT NULL)',
new_version_time_table, new_sequence_name);
EXECUTE format('CREATE UNIQUE INDEX %s ON %s USING btree (version, modified_time ASC NULLS LAST)', idx_name, new_version_time_table);
END IF;
END LOOP;
END $$;
CREATE OR REPLACE FUNCTION public.copy_version_time_to_version_time_by_user_func (id_user integer)
RETURNS void
LANGUAGE plpgsql
VOLATILE
STRICT
SECURITY INVOKER
COST 1
AS $$
DECLARE
version_time_table text := 'public.version_time';
new_version_time_table text;
trigger_name text;
max_version bigint;
has_data_below_max_version INTEGER;
id_user_bucket INTEGER;
migrated_version_time_count INTEGER;
migrated_version_table text;
migrated_version_table_name text;
has_migrated_version_table boolean;
BEGIN
id_user_bucket := CASE WHEN id_user = 0 THEN 0 ELSE (id_user - 1) % 13 + 1 END;
new_version_time_table = 'public.version_time_' || id_user_bucket;
migrated_version_table = 'public.version_time_' || id_user;
migrated_version_table_name = 'version_time_' || id_user;
trigger_name = 'delete_expired_version_time_' || id_user_bucket || '_data_trigger';
has_migrated_version_table := EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = migrated_version_table_name);
IF NOT has_migrated_version_table THEN
EXECUTE format('DROP TRIGGER IF EXISTS %s ON %s CASCADE', trigger_name, new_version_time_table);
EXECUTE format('INSERT INTO %s (version, modified_time) SELECT version, modified_time FROM %s ON CONFLICT (version, modified_time) DO NOTHING',
new_version_time_table, version_time_table);
EXECUTE format('CREATE TRIGGER %s BEFORE INSERT ON %s FOR EACH ROW EXECUTE PROCEDURE public.delete_expired_version_time_data_func()',
trigger_name, new_version_time_table);
ELSE
EXECUTE format('DROP TRIGGER IF EXISTS %s ON %s CASCADE', trigger_name, new_version_time_table);
EXECUTE format('INSERT INTO %s SELECT * FROM %s ON CONFLICT (version, modified_time) DO NOTHING', new_version_time_table, migrated_version_table);
-- check if there are already data under max_version of version_time in the new_version_time_table
SELECT COALESCE(MAX(version), 0) FROM public.version_time INTO max_version;
EXECUTE format('SELECT 1 FROM %s WHERE version <= %L LIMIT 1', new_version_time_table, max_version) INTO has_data_below_max_version;
EXECUTE format('SELECT COUNT(*) FROM %s', migrated_version_table) INTO migrated_version_time_count;
IF migrated_version_time_count < 100000 AND has_data_below_max_version IS NULL THEN
EXECUTE format('INSERT INTO %s (version, modified_time) SELECT version, modified_time FROM %s ON CONFLICT (version, modified_time) DO NOTHING',
new_version_time_table, version_time_table);
END IF;
EXECUTE format('CREATE TRIGGER %s BEFORE INSERT ON %s FOR EACH ROW EXECUTE PROCEDURE public.delete_expired_version_time_data_func()',
trigger_name, new_version_time_table);
IF id_user > 13 THEN
EXECUTE format('DROP TABLE IF EXISTS %s CASCADE', migrated_version_table);
END IF;
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION public.delete_expired_version_time_data_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
count bigint;
version_time text;
BEGIN
version_time = TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME;
EXECUTE format('SELECT count(*) FROM %s', version_time) INTO count;
IF (count > 110000) THEN
EXECUTE format('DELETE FROM %s WHERE version IN (SELECT version FROM %s order by version limit %L)', version_time, version_time, 10000);
END IF;
RETURN NEW;
END
$$;
CREATE OR REPLACE FUNCTION public.create_version_time_by_id_user_trigger_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
id_user integer := NEW.id;
new_version_time_table text;
new_version_time_table_name text;
trigger_name text;
pk_name text;
idx_name text;
new_sequence_name text;
id_user_bucket integer;
BEGIN
id_user_bucket := CASE WHEN id_user = 0 THEN 0 ELSE (id_user - 1) % 13 + 1 END;
new_version_time_table = 'public.version_time_' || id_user_bucket;
new_version_time_table_name = 'version_time_' || id_user_bucket;
pk_name = 'version_id_' || id_user_bucket || '_primary';
idx_name = 'version_time_' || id_user_bucket || '_idx';
new_sequence_name = 'public.version_time_' || id_user_bucket || '_version_seq';
trigger_name = 'delete_expired_version_time_' || id_user_bucket || '_data_trigger';
-- check if the sequence and table already exists
RAISE NOTICE 'Checking if sequence % and table % exist', new_sequence_name, new_version_time_table;
IF EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = new_version_time_table_name
) THEN
RETURN NEW;
END IF;
EXECUTE format('CREATE SEQUENCE %s START WITH 1', new_sequence_name);
EXECUTE format('CREATE TABLE %s (version BIGINT NOT NULL DEFAULT nextval(''%s''), modified_time BIGINT NOT NULL, CONSTRAINT %s PRIMARY KEY (version))',
new_version_time_table, new_sequence_name, pk_name);
EXECUTE format('CREATE INDEX %s ON %s USING btree (modified_time ASC NULLS LAST)', idx_name, new_version_time_table);
EXECUTE format('CREATE TRIGGER %s BEFORE INSERT ON %s FOR EACH ROW EXECUTE PROCEDURE public.delete_expired_version_time_data_func()',
trigger_name, new_version_time_table);
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS create_version_time_by_id_user_trigger ON public.user_info CASCADE;
CREATE TRIGGER create_version_time_by_id_user_trigger
AFTER INSERT ON public.user_info
FOR EACH ROW
EXECUTE PROCEDURE public.create_version_time_by_id_user_trigger_func();
CREATE OR REPLACE FUNCTION public.get_next_version_func (schema text, id_user integer)
RETURNS bigint
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
next_version bigint;
version_table text;
id_user_bucket integer;
BEGIN
id_user_bucket := CASE WHEN id_user = 0 THEN 0 ELSE (id_user - 1) % 13 + 1 END;
version_table = schema || '.version_time_' || id_user_bucket;
EXECUTE 'INSERT INTO ' || version_table || '(modified_time)
SELECT round(extract(epoch FROM now()) * 1000) RETURNING version ' INTO next_version;
RETURN next_version;
END
$$;
CREATE OR REPLACE FUNCTION public.delete_unit_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
count integer;
new_version bigint;
item_table text;
unit_table text;
id_item integer;
id_user integer;
BEGIN
item_table = TG_TABLE_SCHEMA || '.item';
unit_table = TG_TABLE_SCHEMA || '.unit';
IF (TG_OP = 'DELETE') THEN
id_item = OLD.id_item;
EXECUTE format('SELECT id_user, count(*) FROM %s WHERE id_item = %L GROUP BY id_user', unit_table, id_item) INTO id_user, count;
IF (count > 0) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE format('UPDATE %s SET version = %L WHERE id_item = %L', unit_table, new_version, id_item);
ELSE
EXECUTE format('DELETE FROM %s WHERE id = %L', item_table, id_item);
END IF;
EXECUTE format('DELETE FROM index_queue WHERE id_user = %L and id_unit = %L', id_user, OLD.id);
EXECUTE format('SELECT public.unit_update_normal_album_func(%L, %L)', TG_TABLE_SCHEMA, id_item);
EXECUTE format('SELECT public.unit_update_condition_album_func(%L, %L)', TG_TABLE_SCHEMA, id_item);
ELSIF (TG_OP = 'UPDATE' AND OLD.reindex_flag = NEW.reindex_flag) THEN
id_item = OLD.id_item;
IF (NEW.id_item != OLD.id_item) THEN
EXECUTE format('SELECT public.unit_update_id_item_func(%L, %L, %L)', TG_TABLE_SCHEMA, id_item, NEW.id_item);
END IF;
IF (NEW.cache_key != OLD.cache_key) THEN
EXECUTE format('SELECT public.unit_update_normal_album_func(%L, %L)', TG_TABLE_SCHEMA, id_item);
EXECUTE format('SELECT public.unit_update_condition_album_func(%L, %L)', TG_TABLE_SCHEMA, id_item);
END IF;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.delete_normal_album_after_trigger_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
next_version bigint;
delete_album_table text;
share_table text;
condition_album_item_table text;
id_user integer;
BEGIN
delete_album_table = TG_TABLE_SCHEMA || '.delete_album';
share_table = TG_TABLE_SCHEMA || '.share';
IF (TG_OP = 'DELETE') THEN
SELECT id from user_info where id = OLD.id_user INTO id_user;
if (id_user IS NULL) THEN
RETURN NULL;
END IF;
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, OLD.id_user) INTO next_version;
EXECUTE 'INSERT INTO ' || delete_album_table || ' (id_normal_album, id_user, version)
VALUES ( '|| OLD.id || ', ' || OLD.id_user || ', ' || next_version || ')';
IF OLD.passphrase_share IS NOT NULL THEN
EXECUTE format('DELETE FROM %s WHERE passphrase = %L', share_table, OLD.passphrase_share);
END IF;
IF OLD.type = 1 THEN
condition_album_item_table = TG_TABLE_SCHEMA || '.condition_album_item_' || OLD.id;
EXECUTE format('DROP TABLE IF EXISTS %s', condition_album_item_table);
END IF;
END IF;
RETURN NULL;
END;
$$;
CREATE OR REPLACE FUNCTION public.update_unit_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
BEGIN
IF (TG_OP = 'INSERT') THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO new_version;
NEW.version = new_version;
RETURN NEW;
ELSIF (TG_OP = 'UPDATE') THEN
IF (NEW.index_stage != OLD.index_stage OR
NEW.filename != OLD.filename OR
NEW.id_folder != OLD.id_folder OR
NEW.id_user != OLD.id_user OR
NEW.takentime != OLD.takentime OR
NEW.id_geocoding != OLD.id_geocoding OR
NEW.mtime != OLD.mtime OR
NEW.cache_key != OLD.cache_key) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO new_version;
NEW.version = new_version;
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.update_normal_album_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
next_version bigint;
BEGIN
IF (TG_OP = 'INSERT' or (TG_OP = 'UPDATE' AND (NEW.item_count != OLD.item_count OR NEW.cover != OLD.cover OR NEW.name != OLD.name OR NEW.shared != OLD.shared OR NEW.sort_by != OLD.sort_by OR NEW.sort_direction != OLD.sort_direction OR NEW.passphrase_share != OLD.passphrase_share))) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO next_version;
NEW.version = next_version;
END IF;
RETURN NEW;
END
$$;
CREATE OR REPLACE FUNCTION public.face_update_unit_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
unit_table text;
BEGIN
unit_table = TG_TABLE_SCHEMA || '.unit';
IF (TG_OP = 'UPDATE' AND NEW.id_person != OLD.id_person) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO new_version;
EXECUTE 'UPDATE ' || unit_table || ' SET version = ' || new_version || ' WHERE id = ' || NEW.ref_id_unit;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.metadata_update_unit_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
unit_table text;
id_user integer;
BEGIN
unit_table = TG_TABLE_SCHEMA || '.unit';
IF (TG_OP = 'UPDATE') THEN
IF (NEW.description != OLD.description OR NEW.orientation != OLD.orientation) THEN
EXECUTE 'SELECT id_user FROM ' || unit_table || ' WHERE id = ' || NEW.id_unit INTO id_user;
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE 'UPDATE ' || unit_table || ' SET version = ' || new_version || ' WHERE id = ' || NEW.id_unit;
END IF;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.tag_update_unit_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
unit_table text;
id_user integer;
id_unit integer;
BEGIN
unit_table = TG_TABLE_SCHEMA || '.unit';
IF (TG_OP = 'INSERT') THEN
EXECUTE 'SELECT id_user FROM ' || unit_table || ' WHERE id = ' || NEW.id_unit INTO id_user;
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE 'UPDATE ' || unit_table || ' SET version = ' || new_version || ' WHERE id = ' || NEW.id_unit;
ELSIF (TG_OP = 'DELETE') THEN
EXECUTE 'SELECT id_user FROM ' || unit_table || ' WHERE id = ' || OLD.id_unit INTO id_user;
IF (id_user IS NULL) THEN
RETURN NULL;
END IF;
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE 'UPDATE ' || unit_table || ' SET version = ' || new_version || ' WHERE id = ' || OLD.id_unit;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.increase_album_version_func ( schema text, album_id integer)
RETURNS void
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
album_table text;
new_version integer;
item_id integer;
id_user integer;
BEGIN
album_table = schema || '.album';
EXECUTE 'SELECT id_user FROM ' || album_table || ' WHERE id = ' || album_id INTO id_user;
SELECT public.get_next_version_func(schema, id_user) INTO new_version;
EXECUTE format('UPDATE %s SET version = %L WHERE id = %L', album_table, new_version, album_id);
END
$$;
DROP FUNCTION IF EXISTS public.item_type_update_unit_version_func ();
CREATE OR REPLACE FUNCTION public.delete_item_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
delete_item_table text;
new_version bigint;
id_user integer;
BEGIN
delete_item_table = TG_TABLE_SCHEMA || '.delete_item';
SELECT id from user_info where id = OLD.id_user INTO id_user;
if (id_user IS NOT NULL) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE format('INSERT INTO %s (id_item, id_user, version) VALUES (%L, %L, %L)', delete_item_table, OLD.id, OLD.id_user, new_version);
END IF;
IF (OLD.is_similar_top_pick IS TRUE) THEN
PERFORM public.update_similar_group_top_pick_func(OLD.id_similar_group);
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.share_permission_update_album_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
album_table text;
passphrase text;
id_user integer;
BEGIN
album_table = TG_TABLE_SCHEMA || '.album';
IF (TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (NEW.permission != OLD.permission))) THEN
passphrase = NEW.passphrase_share;
id_user = NEW.id_user;
ELSIF (TG_OP = 'DELETE') THEN
passphrase = OLD.passphrase_share;
id_user = OLD.id_user;
END IF;
if passphrase IS NOT NULL THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, id_user) INTO new_version;
EXECUTE format('UPDATE %s SET version = %L WHERE passphrase_share = %L', album_table, new_version, passphrase);
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.share_update_album_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
album_table text;
BEGIN
album_table = TG_TABLE_SCHEMA || '.album';
IF (TG_OP = 'UPDATE') THEN
IF (NEW.privacy_type != OLD.privacy_type OR NEW.modified_time != OLD.modified_time OR NEW.expired_time != OLD.expired_time OR NEW.hashed_password != OLD.hashed_password OR NEW.enable != OLD.enable) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO new_version;
EXECUTE format('UPDATE %s SET version = %L WHERE passphrase_share = %L', album_table, new_version, NEW.passphrase);
END IF;
END IF;
RETURN NULL;
END
$$;
CREATE OR REPLACE FUNCTION public.update_condition_album_version_func ()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 1
AS $$
DECLARE
new_version bigint;
BEGIN
IF (TG_OP = 'INSERT' or
(TG_OP = 'UPDATE' AND
(
NEW.condition::text != OLD.condition::text
OR NEW.item_count != OLD.item_count
OR NEW.cover != OLD.cover
OR NEW.name != OLD.name
OR NEW.shared != OLD.shared
OR NEW.sort_by != OLD.sort_by
OR NEW.sort_direction != OLD.sort_direction
OR NEW.passphrase_share != OLD.passphrase_share
)
)
) THEN
SELECT public.get_next_version_func(TG_TABLE_SCHEMA, NEW.id_user) INTO new_version;
NEW.version = new_version;
END IF;
RETURN NEW;
END
$$;
DROP FUNCTION IF EXISTS public.get_next_version_func (schema text);