Использование Postgres доменов для упрощения проверки ввода функции - PullRequest
1 голос
/ 04 февраля 2020

Используя Postgres 11.5, я смотрю на CREATE DOMAIN со вчерашнего дня и хотел бы уточнить, как они могут / не могут помочь с параметрами функции. В идеале я хотел бы использовать домен для простого просмотра входных параметров, но с полезным ответом об ошибке. В качестве примера я использую простой пример, который блокирует пустые и пустые строки:

CREATE DOMAIN text_not_empty AS
    text
    NOT NULL
    CHECK (value <> '');

Я опробовал это как тип поля для таблицы, и это здорово. Когда мы не разрешаем пустые строки, это кажется простым способом реализации ограничения без отдельного правила или триггера. Я надеюсь получить аналогичные преимущества по параметрам функции. Однако с функциями нам действительно нужны четкие сообщения об ошибках, так как вызывающий может быть из Node или из какой-либо другой среды.

В качестве примера, вот фиктивная функция, которая требует строку, используя явную проверку на входе:

CREATE OR REPLACE FUNCTION api.test_this_function(in_text text)
 RETURNS int
 LANGUAGE plpgsql
AS $function$

BEGIN

IF in_text = '' THEN
    RAISE EXCEPTION USING
        message = 'Input text must be supplied',
        detail  = 'Deets',
        hint    = 'Supply a search string',
        errcode = 'KC123'; -- Custom code
END IF;

    RETURN 1;

END;
$function$

Это отлично работает, но я надеюсь, что домен может упростить такие случаи. Насколько я могу судить, домен не улучшит , так как у функции никогда не будет возможности получить ошибку. Если я прочитал документы и правильно понял мои эксперименты, вы можете только RAISE в пределах a BEGIN...END блока. Если да, то как люди рекомендуют проверять исходные данные? И я упускаю возможность с доменами?

Чтобы вывести sh из того, на чем я основываю свои впечатления, вот функция, которая использует проверенный на основе домена, а также (глупый) пользовательский check:

CREATE OR REPLACE FUNCTION api.domain_test(in_text text_not_empty)
     RETURNS timestamptz

AS $BODY$

BEGIN

IF in_text = 'foo' THEN
    RAISE EXCEPTION USING
        message = 'Invalid search string',
        hint = 'Supply a search string other than ''foo''.',
        errcode = 'KC123'; -- Custom code
END IF;

    RETURN now();

END;

$BODY$
 LANGUAGE plpgsql;

Таким образом, он не должен работать ни с одним параметром, с нулевым параметром, пустой строкой или строкой 'foo'. В противном случае он должен вернуть метку времени. Я опробовал эти пять случаев, показанных здесь:

select * from domain_test();      -- 1 : Fails to reach the method.
select * from domain_test(null);  -- 2 : Fails before entering method.
select * from domain_test('');    -- 3 : Fails before entering method.
select * from domain_test('foo'); -- 4 : Fails on custom exception.
select * from domain_test('a');   -- 5 : Succeeds.

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

Я не спрашиваю конкретизировать c вопрос кода, но было бы очень полезно, если бы кто-то мог подтвердить, что моя модель того, как ошибки улавливаются и обрабатываются, является правильной и полной. Как только я пойму, как Postgres «думает» об этом, мне будет легче рассуждать об этом тоже.

Call paths

null и пустые строковые регистры никогда не попадают в блок BEGIN, поэтому, похоже, нет способа использовать RAISE для настройки message, hint и т. Д. c. Есть ли глобальный обработчик ошибок или более широкая система catch, которую я пропускаю?

Относительно ORM

wildplasser предложил несколько комментариев об ORM и стратегиях, которые дают понять, что я не сделал не могу объяснить фон здесь. Я не хотел увязывать вопрос более подробно, но я полагаю, что добавлю некоторые пояснения.

Мы не собираемся использовать ORM. Кажется, что это добавляет другой уровень модели / абстракции, чтобы помочь людям привыкнуть к другому языку. Для меня это просто сложность, от которой я ничего не получаю, в данном случае. Я предпочел бы писать запросы прямо SQL без большого количества строительных лесов. Идея состоит в том, чтобы вывести sh логику запроса / SQL в Postgres. Тогда есть одно место для логики c.

План состоит в том, чтобы сделать наш API запросов PG серией функций с определенными входами / выходами. Я могу захватить или сохранить эти детали, используя pg_proc, information_schema.parameters и пользовательскую таблицу для определения правил параметров (разрешенные / исключенные значения, серии или диапазоны). Это большое количество лесов должно помочь, поскольку его довольно легко механизировать. Используя данные ввода / вывода, я могу автоматически генерировать объявления ввода / вывода, проверять код (над чем я здесь работаю), документацию и контрольные примеры. Фактическое тело запроса? Я напишу это от руки. Написание умного построителя запросов, который вычисляет все мои joins et c.? Postgres лучше в этом сейчас, когда я когда-нибудь буду ... огромной задачей, я бы сделал дерьмовую работу. Таким образом, я могу написать тело запроса вручную, передать его планировщику / оптимизатору PG и настроить при необходимости. Он находится в чёрном ящике, поэтому внешние клиенты не пострадают от внутренних изменений.

Уровень HTTP API будет написан на двух языках для начала, возможно, с большим количеством языков, вероятно, с большим количеством диалектов. Затем Узел и др. c. инструменты могут обрабатывать вызовы маршрутизации и функций в своей собственной форме. последнее , которое я хочу сделать, - это вынести sh реализацию логики запросов c в избыточные реализации на разных языках. Кошмар на стольких уровнях. Для записи, функции будут в основном RETURN TABLE, определенные в строке или через CREATE TYPE.

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Я бы сказал, что вы все правильно поняли.

Если указанное значение не проходит проверку для домена, функция даже не вызывается. Это особенность: он централизует такие тесты в определении домена, так что вам не нужно повторять их повсеместно, и это экономит затраты на фактический вызов функции.

Я считаю, по крайней мере, Второе сообщение об ошибке очень полезно:

ERROR:  value for domain text_not_empty violates check constraint "text_not_empty_check"

ERROR:  domain text_not_empty does not allow null values

Если это не достаточно ясно для вас, и вы не против написать C, вы можете написать свой собственный тип данных и иметь необычные сообщения об ошибках в типе функция ввода.

0 голосов
/ 04 февраля 2020

Ваши выводы точны, ошибка

select domain_test('');

ERROR:  value for domain text_not_empty violates check constraint "text_not_empty_check"

возникает на этапе разрешения типов аргументов функции, поэтому функция никогда не выполняется. Если ваша цель - настроить сообщение об ошибке, настраиваемый домен вам не поможет.

Нет глобального обработчика ошибок. Единственный вариант - вызывать функцию внутри другого блока кода

do $$
begin
    select domain_test('');
exception when others then
    raise exception 'my error message';
end $$;

ERROR:  my error message
CONTEXT:  PL/pgSQL function inline_code_block line 5 at RAISE

Кажется, что ваш оригинальный подход без пользовательского домена имеет больше смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...