Я изо всех сил пытаюсь выяснить, как лучше всего обработать возврат результатов или ошибок в мое приложение из хранимых функций Postgres.
Рассмотрим следующий придуманный пример psudeocode:
app.get_resource(_username text)
RETURNS <???>
BEGIN
IF ([ ..user exists.. ] = FALSE) THEN
RETURN 'ERR_USER_NOT_FOUND';
END IF;
IF ([ ..user has permission.. ] = FALSE) THEN
RETURN 'ERR_NO_PERMISSION';
END IF;
-- Return the full user object.
RETURN QUERY( SELECT 1
FROM app.resources
WHERE app.resources.owner = _username);
END
Функция может завершиться с ошибкой или завершиться успешно и вернуть 0 или более ресурсов.
Сначала я попытался создать собственный тип, который будет всегда использоваться в качестве стандартного возвращаемого типа в каждой функции:
CREATE TYPE app.appresult AS (
success boolean,
error text,
result anyelement
);
Postgres не позволяет этого, однако:
[42P16] ERROR: column "result" has pseudo-type anyelement
Затем я обнаружил параметры OUT и предпринял следующие попытки:
CREATE OR REPLACE FUNCTION app.get_resource(
IN _username text,
OUT _result app.appresult -- Custom type
-- {success bool, error text}
)
RETURNS SETOF record
AS
$$
BEGIN
IF 1 = 1 THEN -- just a test
_result.success = false;
_result.error = 'ERROR_ERROR';
RETURN NULL;
END IF;
RETURN QUERY(SELECT * FROM app.resources);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
Postgres тоже не нравится:
[42P13] ERROR: function result type must be app.appresult because of OUT parameters
Также пробовал аналогичную функцию, но в обратном порядке: возвращал пользовательский объект app.appresult и устанавливал для параметра OUT значение «SETOF RECORD». Это также было запрещено.
Наконец, я изучил обработку исключений в Postgres, используя
RAISE EXCEPTION 'ERR_MY_ERROR';
Итак, в примере функции я бы просто поднял эту ошибку и вернулся.
Это привело к тому, что драйвер отправил обратно ошибку как:
"ERROR: ERR_MY_ERROR\nCONTEXT: PL/pgSQL function app.test(text) line 6 at RAISE\n(P0001)"
Это достаточно просто для анализа, но делать что-то не так.
Как лучше всего решить эту проблему?
Можно ли иметь собственный объект AppResult, который я мог бы вернуть?
Что-то вроде:
{ success bool, error text, result <whatever type> }
// Редактировать 1 //
Я думаю, что больше склоняюсь к решению @Laurenz Albe.
Моя основная цель проста: вызвать хранимую процедуру, которая может вернуть либо ошибку, либо некоторые данные.
Похоже, что с помощью RAISE это достигается, а драйвер C ++ позволяет легко проверять состояние ошибки, возвращаемое запросом.
if ([error code returned from the query] == 90100)
{
// 1. Parse out my overly verbose error from the raw driver
// error string.
// 2. Handle the error.
}
Мне также интересно использовать собственные коды SQLSTATE вместо анализа строки драйвера.
Бросок «__404» может означать, что во время выполнения моих SP он не мог продолжаться, потому что не была найдена нужная запись.
Когда я вызываю функцию sql из моего приложения, у меня есть общее представление о том, что означает сбой с '__404' и как с этим справиться. Это позволяет избежать дополнительного шага разбора строки ошибки драйвера.
Я также вижу вероятность того, что это плохая идея.
Чтение перед сном:
https://www.postgresql.org/docs/current/static/errcodes-appendix.html