Простое развертывание и откат функций PostgreSQL с помощью Supabase
В сфере управления базами данных решающее значение имеют контроль версий и развертывание. Эффективное развертывание функций базы данных и управление ими жизненно важно для поддержания целостности ваших приложений, управляемых данными. Хотя миграция баз данных, как подробно описано в руководстве по миграции Supabase, идеально подходит для долгосрочных проектов, существуют сценарии, такие как создание прототипов и быстрая разработка, где вам нужна большая гибкость.
В этом блоге мы рассмотрим подход, предназначенный для быстрого прототипирования и гибкой разработки — как легко развертывать и откатывать функции PostgreSQL с помощью Supabase. Supabase, мощная альтернатива традиционным системам управления базами данных с открытым исходным кодом, упрощает процесс развертывания функций и управления ими в этих сценариях.
Если вы работаете над более сложными рабочими процессами или долгосрочными проектами, мы настоятельно рекомендуем обратиться к руководству по миграции Supabase для оптимального контроля версий и методов развертывания.
PostgreSQL и Supabase в современных веб-приложениях
PostgreSQL, надежная система управления реляционными базами данных (СУБД) с открытым исходным кодом, завоевала популярность в веб-разработке благодаря своей надежности, расширяемости и поддержке сложных типов данных.
Supabase, платформа с открытым исходным кодом, предлагает различные инструменты и услуги для современных веб-приложений. Он использует PostgreSQL в качестве основного ядра базы данных и предоставляет удобный интерфейс для управления данными, аутентификации и многого другого.
Мы рассмотрим, как Supabase дополняет PostgreSQL, упрощая развертывание функций и откат. Вы можете обратиться к документации PostgreSQL, чтобы узнать больше о PostgreSQL.
Отслеживание истории функций в PostgreSQL
При управлении базой данных PostgreSQL важно отслеживать изменения, вносимые в функции с течением времени. Эта историческая запись позволяет вам просматривать, проверять и при необходимости возвращаться к предыдущим версиям.
Чтобы облегчить это, мы создадим archive.function_history
таблицу, в которой будет храниться важная информация о каждой функции, включая ее имя, аргументы, тип возвращаемого значения, исходный код и языковые настройки.
Вот код SQL для создания этой таблицы:
CREATE SCHEMA archive;
CREATE TABLE archive.function_history (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
schema_name text,
function_name text,
args text,
return_type text,
source_code text,
lang_settings text,
updated_at timestampz DEFAULT now(),
version NUMERIC DEFAULT 1,
CONSTRAINT function_history_pkey PRIMARY KEY (id)
);
--Handling version numbers automatically:
CREATE OR REPLACE FUNCTION calculate_version()
RETURNS TRIGGER AS $$
BEGIN
-- Calculate the version number for new rows
SELECT COALESCE(MAX(version), 0) + 1
INTO NEW.version
FROM archive.function_history
WHERE schema_name = NEW.schema_name
AND function_name = NEW.function_name
AND return_type = NEW.return_type
AND args = NEW.args;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_insert_function_history
BEFORE INSERT ON archive.function_history
FOR EACH ROW
EXECUTE FUNCTION calculate_version();
Сохранение истории функций
Функция
archive.save_function_history
Чтобы автоматизировать изменения функций записи, мы создадим функцию PostgreSQL с именем archive.save_function_history
. Эта функция принимает такие параметры, как function_name, args, return_type, source_code, schema_name, и lang_settings.
Вот SQL-код для создания archive.save_function_history
функции:
CREATE OR REPLACE
FUNCTION archive.save_function_history(
function_name text,
args text,
return_type text,
source_code text,
schema_name text default 'public',
lang_settings text default 'plpgsql'
) RETURNS void
SET search_path = public, archive
SECURITY DEFINER
AS
$$
BEGIN
INSERT INTO archive.function_history (
schema_name,
function_name,
args,
return_type,
source_code,
lang_settings)
VALUES (schema_name, function_name, args, return_type, source_code, lang_settings);
END;
$$
LANGUAGE plpgsql;
-- Protecting the function:
REVOKE EXECUTE ON FUNCTION
archive.save_function_history FROM public;
REVOKE EXECUTE ON FUNCTION
archive.save_function_history FROM anon, authenticated;
Эта функция позволяет нам легко сохранять снимок функции каждый раз, когда она изменяется.
Развертывание функций из исходного кода
Функция
create_function_from_source
Управление функциями часто предполагает их развертывание из исходного кода. PostgreSQL требует специального синтаксиса для создания функций, а Supabase упрощает это с помощью create_function_from_source
функции.
CREATE OR REPLACE FUNCTION
create_function_from_source(
function_text text,
schema_name text default 'public'
) RETURNS text
SECURITY DEFINER
AS $$
DECLARE
function_name text;
argument_types text;
return_type text;
function_source text;
lang_settings text;
BEGIN
-- Execute the function text to create the function
EXECUTE function_text;
-- Extract function name from function text
SELECT (regexp_matches(function_text, 'create (or replace )?function (public\.)?(\w+)', 'i'))[3]
INTO function_name;
-- Get function details from the system catalog
SELECT pg_get_function_result(p.oid),
pg_get_function_arguments(p.oid), p.prosrc, l.lanname
INTO return_type, argument_types, function_source, lang_settings
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
JOIN pg_language l ON l.oid = p.prolang
WHERE n.nspname = schema_name AND p.proname = function_name;
-- Save function history
PERFORM archive.save_function_history(function_name, argument_types, return_type, function_text, schema_name, lang_settings);
RETURN 'Function created successfully.';
EXCEPTION
WHEN others THEN
RAISE EXCEPTION 'Error creating function: %', sqlerrm;
END;
$$ LANGUAGE plpgsql;
-- Protecting the function:
REVOKE EXECUTE ON FUNCTION
create_function_from_source FROM public;
REVOKE EXECUTE ON FUNCTION
create_function_from_source FROM anon, authenticated;
Эта функция принимает исходный код SQL функции и имя схемы в качестве параметров, создавая функцию в базе данных. Это мощный инструмент для создания динамических функций.
Вот пример развертывания функции с использованием create_function_from_source
:
SELECT create_function_from_source(
$$
-- Note that you can just paste the function below:
CREATE OR REPLACE FUNCTION public.convert_to_uuid(input_value text)
RETURNS uuid
AS $function$
DECLARE
hash_hex text;
BEGIN
-- Return null if input_value is null or an empty string
IF input_value IS NULL OR NULLIF(input_value, '') IS NULL THEN
RETURN NULL;
END IF;
hash_hex := substring(encode(digest(input_value::bytea, 'sha512'), 'hex'), 1, 36);
RETURN (left(hash_hex, 8) || '-' || right(hash_hex, 4) || '-4' || right(hash_hex, 3) || '-a' || right(hash_hex, 3) || '-' || right(hash_hex, 12))::uuid;
END;
$function$
LANGUAGE plpgsql
IMMUTABLE
SECURITY DEFINER;
-- End of the function above
$$
);
Откат функций
Откат функций так же важен, как и их развертывание. Ошибки случаются, и возможность вернуться к предыдущей версии может сэкономить драгоценное время и предотвратить повреждение данных.
Функция rollback_function
приходит на помощь. Он извлекает из таблицы самую последнюю версию функции archive.function_history
и выполняет ее. Если предыдущей версии не существует, она корректно справляется с ситуацией.
Вот код SQL для создания и использования rollback_function
:
CREATE OR REPLACE FUNCTION rollback_function(
func_name text,
schema_n text default 'public'
) RETURNS text
SECURITY DEFINER
AS $$
DECLARE
function_text text;
BEGIN
-- Get the most recent function version from the function_history table
SELECT source_code
INTO function_text
FROM archive.function_history
WHERE function_name = func_name AND schema_name = schema_n
ORDER BY updated_at DESC
LIMIT 1;
-- If no previous version is found, raise an error
IF function_text IS NULL THEN
RAISE EXCEPTION 'No previous version of function % found.', func_name;
END IF;
-- Add 'or replace' to the function text if it's not already there (case-insensitive search and replace)
IF NOT function_text ~* 'or replace' THEN
function_text := regexp_replace(function_text, 'create function', 'create or replace function', 'i');
END IF;
-- Drop current version:
EXECUTE format('DROP FUNCTION IF EXISTS %I.%I', schema_n, func_name);
-- Execute the function text to create the function
EXECUTE function_text;
RETURN 'Function rolled back successfully.';
EXCEPTION
WHEN others THEN
RAISE EXCEPTION 'Error rolling back function: %', sqlerrm;
END;
$$ LANGUAGE plpgsql;
-- Protecting the function:
REVOKE EXECUTE ON FUNCTION rollback_function FROM public;
REVOKE EXECUTE ON FUNCTION rollback_function FROM anon, authenticated;
-- Example of rolling back a function
SELECT rollback_function('convert_to_uuid');
Настройка существующих функций в качестве первой версии
Если вы работаете с существующей базой данных, но хотите начать управлять версиями прямо сейчас. Вы можете использовать эту функцию ниже, чтобы заархивировать все в общедоступной схеме.
CREATE OR REPLACE FUNCTION archive.setup_function_history(schema_name text default 'public')
RETURNS VOID AS
$$
DECLARE
function_record record;
BEGIN
-- Loop through existing functions in the specified schema
FOR function_record IN (
SELECT
n.nspname AS schema_name,
p.proname AS function_name,
pg_catalog.pg_get_function_arguments(p.oid) AS args,
pg_catalog.pg_get_function_result(p.oid) AS return_type,
pg_catalog.pg_get_functiondef(p.oid) AS source_code,
l.lanname AS lang_settings
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang
WHERE n.nspname = schema_name
)
LOOP
-- Insert information about the function into the history table
PERFORM archive.save_function_history(
function_record.function_name,
function_record.args,
function_record.return_type,
function_record.source_code,
function_record.schema_name,
function_record.lang_settings
);
END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT archive.setup_function_history();
Заключение
В заключение, эффективное управление функциями PostgreSQL имеет решающее значение для разработки веб-приложений. Supabase благодаря своей интеграции с PostgreSQL и изученным нами инструментам предлагает упрощенный подход к развертыванию и откату функций.
Ключевые выводы из этой публикации в блоге включают важность отслеживания истории функций, создание таблицы archive.function_history
, archive.save_function_history
функцию записи изменений, а также удобство create_function_from_text
иrollback_function
для развертывания и отката.
Мы рекомендуем вам продолжить изучение Supabase и PostgreSQL, чтобы раскрыть весь потенциал эффективного управления базами данных.
Дополнительные ресурсы
Для получения дополнительной информации и изучения вот несколько дополнительных ресурсов:
- Супабаза Документация
- Документация PostgreSQL
- Репозиторий Supabase на GitHub
- Официальный сайт PostgreSQL
Не стесняйтесь углубляться в эти ресурсы, чтобы лучше понять эти мощные инструменты управления базами данных.