DevGang
Авторизоваться

Простое развертывание и откат функций 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_historyarchive.save_function_historyфункцию записи изменений, а также удобство create_function_from_textиrollback_functionдля развертывания и отката.

Мы рекомендуем вам продолжить изучение Supabase и PostgreSQL, чтобы раскрыть весь потенциал эффективного управления базами данных.

Дополнительные ресурсы

Для получения дополнительной информации и изучения вот несколько дополнительных ресурсов:

Не стесняйтесь углубляться в эти ресурсы, чтобы лучше понять эти мощные инструменты управления базами данных.

Источник:

#PostgreSQL
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу