Содержание
- Проблема при возврате результата в функцию PostgreSQL
- Postgres: возврат результатов или ошибки из сохраненных функций
- Re: Function Syntax Help
- return query and function result type must be real because of OUT parameters
- Responses
- Browse pgsql-general by date
- Postgres: возврат результатов или ошибка из хранимых функций
Проблема при возврате результата в функцию PostgreSQL
Я пришел из мира SQL Server в PostgreSQL (9.0), и у меня возникает проблема при переносе хранимой процедуры/функции. Функция возвращает это сообщение об ошибке:
Мне нужно вернуть оба, результаты запроса и два параметра. Результаты запроса представляются в виде двух столбцов с именем “paramName” и “value” с одной строкой данных для каждого запроса выбора.
Что мне нужно сделать, чтобы передать значения из столбцов, выбранных по запросу, вместе с параметрами OUT в разных наборах результатов, например Transact-SQL, и избежать получения этого сообщения об ошибке?
Это функция pl/pgsql:
Я получаю только результат запроса или параметры out в наборе результатов. Я не могу понять, как использовать оба метода в одном и том же функциональном ответе.
ОБНОВЛЕНИЕ: Выполнено с помощью курсоров, предложенных Эрвином:
Но я получаю сообщение об ошибке:
Значит, я не могу вернуть курсор, когда у меня есть параметры OUT?
Кроме того, происходит return; в предложении IF THEN прекращается функция, как предполагалось?
У вашего кода есть ряд ошибок. Это должно работать:
Решение основной проблемы: используйте RETURN QUERY чтобы на самом деле вернуть результат. SELECT без цели вызвал сообщение об ошибке.
Несколько небрежных ошибок: отсутствующая одинарная кавычка, внештатные запятые, отсутствует END IF; , несколько WHERE …
Вам нужно ‘mycolumnname1’::text строковый литерал в соответствии с объявленным типом вывода: ‘mycolumnname1’::text
Форма val1 := column1 FROM tbl2 WHERE. ; возможно, но обескуражен. Лучше не смешивать plpgsql и SQL-код таким образом. Используйте альтернативу, предоставленную мной (только для одного параметра) или SELECT INTO .
Я бы посоветовал не использовать идентификаторы CAMeL, даже если это разрешено. Если двойные кавычки не преобразуются в нижний регистр.
Источник
Postgres: возврат результатов или ошибки из сохраненных функций
Я изо всех сил пытаюсь понять, как лучше всего обрабатывать возврат результатов или ошибок в мое приложение из сохраненных функций Postgres.
Рассмотрим следующий пример надуманного псевдокода:
Функция может завершиться неудачно с определенной ошибкой или завершиться успешно и вернуть 0 или более ресурсов.
Сначала я попытался создать настраиваемый тип, который всегда будет использоваться в качестве стандартного возвращаемого типа в каждой функции:
Однако Postgres не позволяет этого:
Затем я обнаружил параметры OUT и попытался использовать следующее:
Постгресу это тоже не нравится:
Также попробовал аналогичную функцию, но в обратном порядке: возврат настраиваемого объекта app.appresult и установка параметра OUT на «SETOF RECORD». Это тоже было недопустимо.
Наконец, я изучил обработку исключений Postgres, используя
Итак, в примере функции я просто подниму эту ошибку и вернусь. Это привело к тому, что драйвер отправил ошибку как:
Это достаточно просто для синтаксического анализа, но делать что-то таким образом кажется неправильным.
Как лучше всего решить эту проблему? Возможно ли иметь собственный объект AppResult, который я мог бы вернуть?
Думаю, я больше склоняюсь к решению @Laurenz Albe.
Моя основная цель проста: вызвать хранимую процедуру, которая может вернуть либо ошибку, либо некоторые данные.
Использование RAISE, похоже, позволяет добиться этого, а драйвер C++ позволяет легко проверять состояние ошибки, возвращаемое из запроса.
Мне также интересно использовать пользовательские коды SQLSTATE вместо анализа строки драйвера.
Выброс «__404» может означать, что в ходе выполнения моих служебных программ он не может продолжаться, потому что не была найдена необходимая запись.
При вызове функции sql из моего приложения у меня есть общее представление о том, что означает сбой с ‘__404’ и как с этим справиться. Это позволяет избежать дополнительного этапа анализа строки ошибки драйвера.
Я также вижу потенциал того, что это плохая идея.
Вызов исключения в случае ошибки является правильное решение






Это немного основано на мнении, но я думаю, что выдача ошибки — лучшее и наиболее элегантное решение. Вот для чего нужны ошибки!
Чтобы различать различные сообщения об ошибках, вы можете использовать SQLSTATE, которые начинаются с 6, 8 или 9 (они не используются), тогда вам не нужно зависеть от формулировки сообщения об ошибке.
Вы можете вызвать такую ошибку с помощью
Мы делаем что-то похожее на то, что вы пытаетесь сделать, но мы используем TEXT , а не ANYELEMENT , потому что (почти?) Любой тип может быть преобразован в TEXT и обратно. Итак, наш тип выглядит примерно так:
(errors our_error_type[], result TEXT)
Функция, которая возвращает это, сохраняет ошибки в массиве errors (это просто какой-то настраиваемый тип ошибки) и может сохранять результат (преобразованный в текст) в поле result .
Вызывающая функция знает, какой тип она ожидает, поэтому она может сначала проверить массив errors , чтобы увидеть, были ли возвращены какие-либо ошибки, а если нет, она может привести значение result к ожидаемому типу возврата.
В качестве общего наблюдения я считаю, что исключения более элегантны (возможно, потому, что я исхожу из опыта работы с C#). Единственная проблема заключается в том, что обработка исключений plpgsql (относительно) медленная, поэтому это зависит от контекста — если вы запускаете что-то много раз в цикле, я бы предпочел решение, которое не использует обработку исключений; если это одиночный вызов и / или особенно когда вы хотите его прервать, я предпочитаю создавать исключение. На практике мы используем и то, и другое на разных этапах стека вызовов.
И, как отметила Лоренц Альбе, вы не предназначены для «синтаксического анализа» исключений, а скорее для создания исключения с определенными значениями в определенных полях, которые функция, перехватывающая исключение, может затем извлекать и действовать напрямую.
В качестве примера:
Я воссоздал настраиваемый тип возвращаемого значения как и попытался выбрать строки из таблицы в поле результата: gist.github.com/Think7/7d30047c73e4c41d78b293a85127dee2 Результат: «(f, MY_ERROR, Wendy)», возвращенный драйвером в виде строки . Мне нужен весь набор результатов, возвращенный в приложение.
@Andrew Если вы хотите, чтобы он возвращал одну строку, выполните: SELECT resources::TEXT FROM app.resources INTO vReturn.result; . Это выбирает всю запись в нераскрытом состоянии (не технический термин). Затем вызывающий код может вернуть его и при желании расширить для доступа к отдельным полям. Однако, если вы хотите вернуть несколько строк, это не сработает (если вы не объедините их, но тогда это станет глупо), и вам лучше использовать исключения.
@Andrew См. Отредактированный пример того, как это работает.
Источник
Re: Function Syntax Help
| From: | «Plugge, Joe R(dot)» |
|---|---|
| To: | Brian Modra |
| Cc: | «pgsql-sql(at)postgresql(dot)org» |
Subject: Re: Function Syntax Help Date: 2009-10-30 19:50:42 Message-ID: BD69807DAE0CE44CA00A8338D0FDD083534BA949@oma00cexmbx03.corp.westworlds.com Views: Raw Message | Whole Thread | Download mbox | Resend email Thread: Lists: pgsql-sql
Thanks, I changed my code to this, it compiled, and it seems to be running now:
CREATE OR REPLACE FUNCTION gen_simultaneous_calls(mystart timestamp, mystop timestamp) RETURNS VOID AS $$
DECLARE
mycount integer;
newstart timestamp := mystart;
newstop timestamp := mystop;
BEGIN
WHILE newstart newstop;
INSERT INTO simultaneous_calls_rpt (startdate,call_count) VALUES (newstart,mycount);
newstart := newstart + INTERVAL ‘1 minute’;
newstop := newstop + INTERVAL ‘1 minute’;
END LOOP;
END;
$$ LANGUAGE ‘plpgsql’ VOLATILE;
From: epailty(at)googlemail(dot)com [mailto:epailty(at)googlemail(dot)com] On Behalf Of Brian Modra
Sent: Friday, October 30, 2009 2:46 PM
To: Plugge, Joe R.
Cc: pgsql-sql(at)postgresql(dot)org
Subject: Re: [SQL] Function Syntax Help
2009/10/30 Plugge, Joe R. >
Thanks Brian, I changed it to this:
CREATE FUNCTION gen_simultaneous_calls(mystart timestamp, mystop timestamp) RETURNS VOID AS $$
DECLARE
mycount integer;
BEGIN
WHILE mystart mystop;
INSERT INTO simultaneous_calls_rpt (startdate,call_count) VALUES (mystart,mycount);
mystart := mystart + INTERVAL ‘1 minute’;
mystop := mystop + INTERVAL ‘1 minute’;
END LOOP;
END;
$$ LANGUAGE ‘plpgsql’ STABLE;
But now am getting a different error:
]$ cat gen_simultaneous_calls.sql | psql holly
ERROR: «$1» is declared CONSTANT
CONTEXT: compilation of PL/pgSQL function «gen_simultaneous_calls» near line 7
mystart and mystop are constants.
you could declare variables and copy those into them, and the modify the new variables.
From: epailty(at)googlemail(dot)com [mailto:epailty(at)googlemail(dot)com ] On Behalf Of Brian Modra
Sent: Friday, October 30, 2009 2:29 PM
To: Plugge, Joe R.
Cc: pgsql-sql(at)postgresql(dot)org
Subject: Re: [SQL] Function Syntax Help
2009/10/30 Plugge, Joe R. >
I am trying to create a function that will grind through a cdr table and populate another table. I am trying to load the function and am getting the following error:
ERROR: function result type must be specified
CREATE FUNCTION gen_simultaneous_calls(mystart timestamp, mystop timestamp) AS $$ DECLARE
you need to tell it the return type. If there is none, «returns void»
e.g.
CREATE FUNCTION gen_simultaneous_calls(mystart timestamp, mystop timestamp) returns void AS $$
WHILE mystart mystop;
INSERT INTO simultaneous_calls_rpt (startdate,call_count) VALUES (mystart,mycount);
mystart := mystart + INTERVAL ‘1 minute’;
mystop := mystop + INTERVAL ‘1 minute’;
$$ LANGUAGE ‘plpgsql’ STABLE;
Joe R. Plugge
Database Administrator, West Interactive Corporation
11650 Miracle Hills Drive, Omaha NE 68154
402-716-0349 | Cell 402-517-2710 | jrplugge(at)west(dot)com
This electronic message transmission, including any attachments, contains information from West Corporation which may be confidential or privileged. The information is intended to be for the use of the individual or entity named above. If you are not the intended recipient, be aware that any disclosure, copying, distribution or use of the contents of this information is prohibited.
If you have received this electronic transmission in error, please notify the sender immediately by a «reply to sender only» message and destroy all electronic and hard copies of the communication, including attachments.
Источник
return query and function result type must be real because of OUT parameters
| From: | Ivan Sergio Borgonovo |
|---|---|
| To: | pgsql-general(at)postgresql(dot)org |
| Subject: | return query and function result type must be real because of OUT parameters |
| Date: | 2008-12-24 11:29:02 |
| Message-ID: | 20081224122902.2a7ef568@dawn.webthatworks.it |
| Views: | Raw Message | Whole Thread | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-general |
I was doing some test to track down an error in a more
complicated function and I stumbled in this behaviour I can’t
understand:
create or replace function test.test(out _rank float4) returns setof
record as $$
begin
return query select 1::float4;
return;
end;
$$ language plpgsql stable;
ERROR: function result type must be real because of OUT parameters
ERROR: function result type must be real because of OUT parameters
SQL state: 42P13
same with text etc.
Responses
- Re: return query and function result type must be real because of OUT parameters at 2008-12-24 14:19:22 from Pavel Stehule
Browse pgsql-general by date
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Pavel Stehule | 2008-12-24 14:19:22 | Re: return query and function result type must be real because of OUT parameters |
| Previous Message | Ivan Sergio Borgonovo | 2008-12-24 08:05:40 | Re: How to Import Excel Into PostgreSQL database |
Copyright © 1996-2023 The PostgreSQL Global Development Group
Источник
Postgres: возврат результатов или ошибка из хранимых функций
Я изо всех сил пытаюсь понять, как наилучшим образом обработать возврат результатов или ошибок к моему приложению из хранимых функций Postgres.
Рассмотрим следующий надуманный пример psudeocode:
Функция может выйти из строя с определенной ошибкой или преуспеть и вернуть 0 или более ресурсов.
Сначала я попытался создать настраиваемый тип, который всегда будет использоваться в качестве стандартного типа возврата в каждой функции:
Postgres этого не позволяет:
Затем я обнаружил параметры OUT и попытался выполнить следующие действия:
Postgres тоже не нравится:
Также попробовали аналогичную функцию, но изменили ее на противоположную сторону: возврат пользовательского объекта app.appresult и установка параметра OUT в «SETOF RECORD». Это также не допускалось.
Наконец, я рассмотрел обработку исключений Postgres, используя
Таким образом, в функции примера я просто подниму эту ошибку и вернусь. Это привело к тому, что драйвер отправил ошибку:
Это достаточно просто, чтобы разобрать, но делать то, что чувствует себя неправильно.
Каков наилучший способ решить эту проблему? Возможно ли иметь собственный объект AppResult, который я мог бы вернуть?
Я думаю, что я больше склоняюсь к решению @Laurenz Albe.
Моя основная цель проста: вызвать хранимую процедуру, которая может вернуть либо ошибку, либо некоторые данные.
Использование RAISE, похоже, достигает этого, а драйвер C++ позволяет легко проверять условие ошибки, возвращаемое из запроса.
Я также интересуюсь использованием пользовательских кодов SQLSTATE вместо разбора строки драйвера.
Бросание «__404» может означать, что во время выполнения моего SPs оно не могло продолжаться, потому что некоторая запись не была найдена.
Когда вы вызываете функцию sql из моего приложения, у меня есть общее представление о том, что она не с помощью «__404» означает и как ее обрабатывать. Это позволяет избежать дополнительного этапа разбора строки ошибки драйвера.
Я также вижу потенциал этого плохой идеи.
Это немного основано на мнениях, но я думаю, что бросание ошибки — лучшее и самое изящное решение. Для этого нужны ошибки!
Чтобы различать различные сообщения об ошибках, вы можете использовать SQLSTATE, которые начинаются с 6, 8 или 9 (они не используются), тогда вам не нужно зависеть от формулировки сообщения об ошибке.
Вы можете поднять такую ошибку с помощью
Мы делаем что-то похожее на то, что вы пытаетесь сделать, но мы используем TEXT а не ANYELEMENT , потому что (почти?) Любой тип может быть ANYELEMENT в TEXT и обратно. Поэтому наш тип выглядит примерно так:
(errors our_error_type[], result TEXT)
Функция, которая возвращает это, хранит ошибки в массиве errors (это всего лишь некоторый произвольный тип ошибки) и может сохранять результат (преобразование в текст) в поле result .
Вызывающая функция знает, какой тип она ожидает, поэтому она может сначала проверить массив errors чтобы увидеть, были ли возвращены какие-либо ошибки, а если нет, то можно применить значение result к ожидаемому типу возврата.
Как общее замечание, я думаю, что исключения более элегантны (возможно, потому, что я исхожу из aС# фона). Единственная проблема заключается в том, что обработка исключений plpgsql (относительно) медленная, поэтому она зависит от контекста — если вы выполняете что-то много раз в цикле, я бы предпочел решение, которое не использует обработку исключений; если это один вызов и/или особенно, когда вы хотите его прервать, я предпочитаю создавать исключение. На практике мы используем оба в разных точках в наших столах вызовов.
И, как отметил Лоренц Албе, вы не должны «разбирать» исключения, так же как возбуждать исключение с определенными значениями в определенных полях, которые функция, которая ловит исключение, может извлекать и действовать напрямую.
Источник
I am new to triggers. I am trying to create a trigger by following this link — http://www.postgresqltutorial.com/creating-first-trigger-postgresql/. But it gives some error. The code block and the error is given below.
Code block:
CREATE OR REPLACE FUNCTION log_last_name_changes()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.last_name <> OLD.last_name THEN
INSERT INTO employee_audits(employee_id,last_name,changed_on)
VALUES(OLD.id,OLD.last_name,now());
END IF;
RETURN NEW;
END;
$BODY$
And the error:
ERROR: no language specified
SQL state: 42P13
What can I try next?
![]()
halfer
19.7k17 gold badges94 silver badges182 bronze badges
asked Sep 21, 2016 at 4:01
![]()
Try this way:
CREATE OR REPLACE FUNCTION log_last_name_changes()
RETURNS trigger AS
$BODY$
BEGIN
IF NEW.last_name <> OLD.last_name THEN
INSERT INTO employee_audits(employee_id,last_name,changed_on)
VALUES(OLD.id,OLD.last_name,now());
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE -- Says the function is implemented in the plpgsql language; VOLATILE says the function has side effects.
COST 100; -- Estimated execution cost of the function.
Haroldo_OK
6,3363 gold badges41 silver badges79 bronze badges
answered Sep 21, 2016 at 4:03
![]()
5
Вопрос:
Я пришел из мира SQL Server в PostgreSQL (9.0), и у меня возникает проблема при переносе хранимой процедуры/функции. Функция возвращает это сообщение об ошибке:
SQLSTATE: 42601;
SQLERRM: query has no destination for result data
Мне нужно вернуть оба, результаты запроса и два параметра. Результаты запроса представляются в виде двух столбцов с именем “paramName” и “value” с одной строкой данных для каждого запроса выбора.
Что мне нужно сделать, чтобы передать значения из столбцов, выбранных по запросу, вместе с параметрами OUT в разных наборах результатов, например Transact-SQL, и избежать получения этого сообщения об ошибке?
Это функция pl/pgsql:
CREATE OR REPLACE FUNCTION myplfunction(
IN i_param1 character varying,
IN i_param2 character varying,
IN i_param3 character varying,
IN i_param4 character varying,
OUT o_call_status integer,
OUT o_call_message character varying)
RETURNS SETOF RECORD AS
$BODY$
DECLARE
val1 varchar;
val2 varchar;
val4 varchar;
BEGIN
-- A couple of IF THEN ommited here
IF (v_solution_id IS NULL) THEN
val1 := (Select column1 FROM tbl2 WHERE column2= i_param1);
IF(val1 IS NULL) THEN
o_call_status := 1005;
o_call_message := column1 is not configured or invalid';
RETURN;
END IF;
END IF;
SELECT 'mycolumnname1' as paramName,mycolumn1 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname2' as paramName,mycolumn2 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname3' as paramName,mycolumn3 as value FROM tb2
WHERE column1 = val1 AND
column4 = val4;
o_call_status := 0;
o_call_message := '';
RETURN;
EXCEPTION WHEN OTHERS THEN
o_call_message := SQLERRM;
o_call_status := SQLSTATE;
end;
Я получаю только результат запроса или параметры out в наборе результатов. Я не могу понять, как использовать оба метода в одном и том же функциональном ответе.
ОБНОВЛЕНИЕ: Выполнено с помощью курсоров, предложенных Эрвином:
CREATE OR REPLACE FUNCTION myplfunction(
IN i_param1 character varying,
IN i_param2 character varying,
IN i_param3 character varying,
IN i_param4 character varying,
OUT o_call_status integer,
OUT o_call_message character varying)
RETURNS refcursor AS
$BODY$
DECLARE
val1 varchar;
val2 varchar;
query_cursor refcursor;
BEGIN
-- A couple of IF THEN ommited here
IF (v_solution_id IS NULL) THEN
val1 := (Select column1 FROM tbl2 WHERE column2= i_param1);
IF(val1 IS NULL) THEN
o_call_status := 1005;
o_call_message := column1 is not configured or invalid';
RETURN;
END IF;
END IF;
open query_cursor for SELECT 'mycolumnname1' as paramName,
mycolumn1 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname2' as paramName,mycolumn2 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname3' as paramName,mycolumn3 as value FROM tb2
WHERE column1 = val1 AND
column4 = val4;
o_call_status := 0;
o_call_message := '';
RETURN query_cursor;
EXCEPTION WHEN OTHERS THEN
o_call_message := SQLERRM;
o_call_status := SQLSTATE;
end;
select * from myplfunction(param1,param2,param3,param4);
Но я получаю сообщение об ошибке:
ERROR: function result type must be record because of OUT parameters
SQL state: 42P13
Значит, я не могу вернуть курсор, когда у меня есть параметры OUT?
Кроме того, происходит return; в предложении IF THEN прекращается функция, как предполагалось?
Ответ №1
У вашего кода есть ряд ошибок. Это должно работать:
CREATE OR REPLACE FUNCTION myplfunction(i_param1 text, i_param2 text
, i_param3 text, i_param4 text)
RETURNS TABLE(param_name text, param_value text) AS
$func$
DECLARE
val1 text;
val2 text;
o_call_status integer; o_call_message text; -- without purpose
BEGIN
IF v_solution_id IS NULL THEN
val1 := (SELECT column1 FROM tbl2 WHERE column2 = i_param1);
IF val1 IS NULL THEN
o_call_status := 1005;
o_call_message := 'column1 is not configured or invalid';
-- I suggest this alternative:
RAISE EXCEPTION 'column1 is not configured or invalid';
END IF;
END IF;
RETURN QUERY
SELECT 'mycolumnname1'::text, mycolumn1 FROM tb1 WHERE column1 = val -- val?
UNION ALL
SELECT 'mycolumnname2', mycolumn2 FROM tb1 WHERE column1 = val -- val?
UNION ALL
SELECT 'mycolumnname3', mycolumn2 FROM tb2 WHERE column2 = val2
AND tb2paramName4 = i_val3;
o_call_status := 0;
o_call_message := '';
EXCEPTION WHEN OTHERS THEN
o_call_message := SQLERRM;
o_call_status := SQLSTATE; -- without purpose
END
$func$ LANGUAGE plpgsql;
-
Решение основной проблемы: используйте
RETURN QUERYчтобы на самом деле вернуть результат. SELECT без цели вызвал сообщение об ошибке. -
Несколько небрежных ошибок: отсутствующая одинарная кавычка, внештатные запятые, отсутствует
END IF;, несколькоWHERE… -
Вам нужно
'mycolumnname1'::textстроковый литерал в соответствии с объявленным типом вывода:'mycolumnname1'::text -
Форма
val1 := column1 FROM tbl2 WHERE...;возможно, но обескуражен. Лучше не смешивать plpgsql и SQL-код таким образом. Используйте альтернативу, предоставленную мной (только для одного параметра) илиSELECT INTO. -
Я бы посоветовал не использовать идентификаторы CAMeL, даже если это разрешено. Если двойные кавычки не преобразуются в нижний регистр.
I am struggling to figure out how to best handle the return of results or errors to my application from Postgres stored functions.
Consider the following contrived psudeocode example:
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
The function can fail with a specific error or succeed and return 0 or more resources.
At first I tried creating a custom type to always use as a standard return type in eachh function:
CREATE TYPE app.appresult AS (
success boolean,
error text,
result anyelement
);
Postgres does not allow this however:
[42P16] ERROR: column "result" has pseudo-type anyelement
I then discovered OUT parameters and attempted the following uses:
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 doesn’t like this either:
[42P13] ERROR: function result type must be app.appresult because of OUT parameters
Also tried a similar function but reversed: Returning a custom app.appresult object and setting the OUT param to «SETOF RECORD». This was also not allowed.
Lastly i looked into Postgres exception handling using
RAISE EXCEPTION 'ERR_MY_ERROR';
So in the example function, i’d just raise this error and return.
This resulted in the driver sending back the error as:
"ERROR: ERR_MY_ERRORnCONTEXT: PL/pgSQL function app.test(text) line 6 at RAISEn(P0001)"
This is easy enough to parse but doing things this way feels wrong.
What is the best way to solve this problem?
Is it possible to have a custom AppResult object that i could return?
Something like:
{ success bool, error text, result <whatever type> }
//Edit 1 //
I think I’m leaning more towards @Laurenz Albe solution.
My main goal is simple: Call a stored procedure which can return either an error or some data.
Using RAISE seems to accomplish this and the C++ driver allows easy checking for an error condition returned from a query.
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.
}
I’m also wondering about using custom SQLSTATE codes instead of parsing the driver string.
Throwing ‘__404’ might mean that during the course of my SPs execution, it could not continue because some record needed was not found.
When calling the sql function from my app, i have a general idea of what it failing with a ‘__404’ would mean and how to handle it. This avoids the additional step of parsing driver error string.
I can also see the potential of this being a bad idea.
Bedtime reading:
https://www.postgresql.org/docs/current/static/errcodes-appendix.html
I am struggling to figure out how to best handle the return of results or errors to my application from Postgres stored functions.
Consider the following contrived psudeocode example:
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
The function can fail with a specific error or succeed and return 0 or more resources.
At first I tried creating a custom type to always use as a standard return type in eachh function:
CREATE TYPE app.appresult AS (
success boolean,
error text,
result anyelement
);
Postgres does not allow this however:
[42P16] ERROR: column "result" has pseudo-type anyelement
I then discovered OUT parameters and attempted the following uses:
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 doesn’t like this either:
[42P13] ERROR: function result type must be app.appresult because of OUT parameters
Also tried a similar function but reversed: Returning a custom app.appresult object and setting the OUT param to «SETOF RECORD». This was also not allowed.
Lastly i looked into Postgres exception handling using
RAISE EXCEPTION 'ERR_MY_ERROR';
So in the example function, i’d just raise this error and return.
This resulted in the driver sending back the error as:
"ERROR: ERR_MY_ERRORnCONTEXT: PL/pgSQL function app.test(text) line 6 at RAISEn(P0001)"
This is easy enough to parse but doing things this way feels wrong.
What is the best way to solve this problem?
Is it possible to have a custom AppResult object that i could return?
Something like:
{ success bool, error text, result <whatever type> }
//Edit 1 //
I think I’m leaning more towards @Laurenz Albe solution.
My main goal is simple: Call a stored procedure which can return either an error or some data.
Using RAISE seems to accomplish this and the C++ driver allows easy checking for an error condition returned from a query.
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.
}
I’m also wondering about using custom SQLSTATE codes instead of parsing the driver string.
Throwing ‘__404’ might mean that during the course of my SPs execution, it could not continue because some record needed was not found.
When calling the sql function from my app, i have a general idea of what it failing with a ‘__404’ would mean and how to handle it. This avoids the additional step of parsing driver error string.
I can also see the potential of this being a bad idea.
Bedtime reading:
https://www.postgresql.org/docs/current/static/errcodes-appendix.html
| Error Code | Condition Name |
|---|---|
| Class 00 — Successful Completion | |
00000 |
successful_completion |
| Class 01 — Warning | |
01000 |
warning |
0100C |
dynamic_result_sets_returned |
01008 |
implicit_zero_bit_padding |
01003 |
null_value_eliminated_in_set_function |
01007 |
privilege_not_granted |
01006 |
privilege_not_revoked |
01004 |
string_data_right_truncation |
01P01 |
deprecated_feature |
| Class 02 — No Data (this is also a warning class per the SQL standard) | |
02000 |
no_data |
02001 |
no_additional_dynamic_result_sets_returned |
| Class 03 — SQL Statement Not Yet Complete | |
03000 |
sql_statement_not_yet_complete |
| Class 08 — Connection Exception | |
08000 |
connection_exception |
08003 |
connection_does_not_exist |
08006 |
connection_failure |
08001 |
sqlclient_unable_to_establish_sqlconnection |
08004 |
sqlserver_rejected_establishment_of_sqlconnection |
08007 |
transaction_resolution_unknown |
08P01 |
protocol_violation |
| Class 09 — Triggered Action Exception | |
09000 |
triggered_action_exception |
| Class 0A — Feature Not Supported | |
0A000 |
feature_not_supported |
| Class 0B — Invalid Transaction Initiation | |
0B000 |
invalid_transaction_initiation |
| Class 0F — Locator Exception | |
0F000 |
locator_exception |
0F001 |
invalid_locator_specification |
| Class 0L — Invalid Grantor | |
0L000 |
invalid_grantor |
0LP01 |
invalid_grant_operation |
| Class 0P — Invalid Role Specification | |
0P000 |
invalid_role_specification |
| Class 0Z — Diagnostics Exception | |
0Z000 |
diagnostics_exception |
0Z002 |
stacked_diagnostics_accessed_without_active_handler |
| Class 20 — Case Not Found | |
20000 |
case_not_found |
| Class 21 — Cardinality Violation | |
21000 |
cardinality_violation |
| Class 22 — Data Exception | |
22000 |
data_exception |
2202E |
array_subscript_error |
22021 |
character_not_in_repertoire |
22008 |
datetime_field_overflow |
22012 |
division_by_zero |
22005 |
error_in_assignment |
2200B |
escape_character_conflict |
22022 |
indicator_overflow |
22015 |
interval_field_overflow |
2201E |
invalid_argument_for_logarithm |
22014 |
invalid_argument_for_ntile_function |
22016 |
invalid_argument_for_nth_value_function |
2201F |
invalid_argument_for_power_function |
2201G |
invalid_argument_for_width_bucket_function |
22018 |
invalid_character_value_for_cast |
22007 |
invalid_datetime_format |
22019 |
invalid_escape_character |
2200D |
invalid_escape_octet |
22025 |
invalid_escape_sequence |
22P06 |
nonstandard_use_of_escape_character |
22010 |
invalid_indicator_parameter_value |
22023 |
invalid_parameter_value |
22013 |
invalid_preceding_or_following_size |
2201B |
invalid_regular_expression |
2201W |
invalid_row_count_in_limit_clause |
2201X |
invalid_row_count_in_result_offset_clause |
2202H |
invalid_tablesample_argument |
2202G |
invalid_tablesample_repeat |
22009 |
invalid_time_zone_displacement_value |
2200C |
invalid_use_of_escape_character |
2200G |
most_specific_type_mismatch |
22004 |
null_value_not_allowed |
22002 |
null_value_no_indicator_parameter |
22003 |
numeric_value_out_of_range |
2200H |
sequence_generator_limit_exceeded |
22026 |
string_data_length_mismatch |
22001 |
string_data_right_truncation |
22011 |
substring_error |
22027 |
trim_error |
22024 |
unterminated_c_string |
2200F |
zero_length_character_string |
22P01 |
floating_point_exception |
22P02 |
invalid_text_representation |
22P03 |
invalid_binary_representation |
22P04 |
bad_copy_file_format |
22P05 |
untranslatable_character |
2200L |
not_an_xml_document |
2200M |
invalid_xml_document |
2200N |
invalid_xml_content |
2200S |
invalid_xml_comment |
2200T |
invalid_xml_processing_instruction |
22030 |
duplicate_json_object_key_value |
22031 |
invalid_argument_for_sql_json_datetime_function |
22032 |
invalid_json_text |
22033 |
invalid_sql_json_subscript |
22034 |
more_than_one_sql_json_item |
22035 |
no_sql_json_item |
22036 |
non_numeric_sql_json_item |
22037 |
non_unique_keys_in_a_json_object |
22038 |
singleton_sql_json_item_required |
22039 |
sql_json_array_not_found |
2203A |
sql_json_member_not_found |
2203B |
sql_json_number_not_found |
2203C |
sql_json_object_not_found |
2203D |
too_many_json_array_elements |
2203E |
too_many_json_object_members |
2203F |
sql_json_scalar_required |
2203G |
sql_json_item_cannot_be_cast_to_target_type |
| Class 23 — Integrity Constraint Violation | |
23000 |
integrity_constraint_violation |
23001 |
restrict_violation |
23502 |
not_null_violation |
23503 |
foreign_key_violation |
23505 |
unique_violation |
23514 |
check_violation |
23P01 |
exclusion_violation |
| Class 24 — Invalid Cursor State | |
24000 |
invalid_cursor_state |
| Class 25 — Invalid Transaction State | |
25000 |
invalid_transaction_state |
25001 |
active_sql_transaction |
25002 |
branch_transaction_already_active |
25008 |
held_cursor_requires_same_isolation_level |
25003 |
inappropriate_access_mode_for_branch_transaction |
25004 |
inappropriate_isolation_level_for_branch_transaction |
25005 |
no_active_sql_transaction_for_branch_transaction |
25006 |
read_only_sql_transaction |
25007 |
schema_and_data_statement_mixing_not_supported |
25P01 |
no_active_sql_transaction |
25P02 |
in_failed_sql_transaction |
25P03 |
idle_in_transaction_session_timeout |
| Class 26 — Invalid SQL Statement Name | |
26000 |
invalid_sql_statement_name |
| Class 27 — Triggered Data Change Violation | |
27000 |
triggered_data_change_violation |
| Class 28 — Invalid Authorization Specification | |
28000 |
invalid_authorization_specification |
28P01 |
invalid_password |
| Class 2B — Dependent Privilege Descriptors Still Exist | |
2B000 |
dependent_privilege_descriptors_still_exist |
2BP01 |
dependent_objects_still_exist |
| Class 2D — Invalid Transaction Termination | |
2D000 |
invalid_transaction_termination |
| Class 2F — SQL Routine Exception | |
2F000 |
sql_routine_exception |
2F005 |
function_executed_no_return_statement |
2F002 |
modifying_sql_data_not_permitted |
2F003 |
prohibited_sql_statement_attempted |
2F004 |
reading_sql_data_not_permitted |
| Class 34 — Invalid Cursor Name | |
34000 |
invalid_cursor_name |
| Class 38 — External Routine Exception | |
38000 |
external_routine_exception |
38001 |
containing_sql_not_permitted |
38002 |
modifying_sql_data_not_permitted |
38003 |
prohibited_sql_statement_attempted |
38004 |
reading_sql_data_not_permitted |
| Class 39 — External Routine Invocation Exception | |
39000 |
external_routine_invocation_exception |
39001 |
invalid_sqlstate_returned |
39004 |
null_value_not_allowed |
39P01 |
trigger_protocol_violated |
39P02 |
srf_protocol_violated |
39P03 |
event_trigger_protocol_violated |
| Class 3B — Savepoint Exception | |
3B000 |
savepoint_exception |
3B001 |
invalid_savepoint_specification |
| Class 3D — Invalid Catalog Name | |
3D000 |
invalid_catalog_name |
| Class 3F — Invalid Schema Name | |
3F000 |
invalid_schema_name |
| Class 40 — Transaction Rollback | |
40000 |
transaction_rollback |
40002 |
transaction_integrity_constraint_violation |
40001 |
serialization_failure |
40003 |
statement_completion_unknown |
40P01 |
deadlock_detected |
| Class 42 — Syntax Error or Access Rule Violation | |
42000 |
syntax_error_or_access_rule_violation |
42601 |
syntax_error |
42501 |
insufficient_privilege |
42846 |
cannot_coerce |
42803 |
grouping_error |
42P20 |
windowing_error |
42P19 |
invalid_recursion |
42830 |
invalid_foreign_key |
42602 |
invalid_name |
42622 |
name_too_long |
42939 |
reserved_name |
42804 |
datatype_mismatch |
42P18 |
indeterminate_datatype |
42P21 |
collation_mismatch |
42P22 |
indeterminate_collation |
42809 |
wrong_object_type |
428C9 |
generated_always |
42703 |
undefined_column |
42883 |
undefined_function |
42P01 |
undefined_table |
42P02 |
undefined_parameter |
42704 |
undefined_object |
42701 |
duplicate_column |
42P03 |
duplicate_cursor |
42P04 |
duplicate_database |
42723 |
duplicate_function |
42P05 |
duplicate_prepared_statement |
42P06 |
duplicate_schema |
42P07 |
duplicate_table |
42712 |
duplicate_alias |
42710 |
duplicate_object |
42702 |
ambiguous_column |
42725 |
ambiguous_function |
42P08 |
ambiguous_parameter |
42P09 |
ambiguous_alias |
42P10 |
invalid_column_reference |
42611 |
invalid_column_definition |
42P11 |
invalid_cursor_definition |
42P12 |
invalid_database_definition |
42P13 |
invalid_function_definition |
42P14 |
invalid_prepared_statement_definition |
42P15 |
invalid_schema_definition |
42P16 |
invalid_table_definition |
42P17 |
invalid_object_definition |
| Class 44 — WITH CHECK OPTION Violation | |
44000 |
with_check_option_violation |
| Class 53 — Insufficient Resources | |
53000 |
insufficient_resources |
53100 |
disk_full |
53200 |
out_of_memory |
53300 |
too_many_connections |
53400 |
configuration_limit_exceeded |
| Class 54 — Program Limit Exceeded | |
54000 |
program_limit_exceeded |
54001 |
statement_too_complex |
54011 |
too_many_columns |
54023 |
too_many_arguments |
| Class 55 — Object Not In Prerequisite State | |
55000 |
object_not_in_prerequisite_state |
55006 |
object_in_use |
55P02 |
cant_change_runtime_param |
55P03 |
lock_not_available |
55P04 |
unsafe_new_enum_value_usage |
| Class 57 — Operator Intervention | |
57000 |
operator_intervention |
57014 |
query_canceled |
57P01 |
admin_shutdown |
57P02 |
crash_shutdown |
57P03 |
cannot_connect_now |
57P04 |
database_dropped |
57P05 |
idle_session_timeout |
| Class 58 — System Error (errors external to PostgreSQL itself) | |
58000 |
system_error |
58030 |
io_error |
58P01 |
undefined_file |
58P02 |
duplicate_file |
| Class 72 — Snapshot Failure | |
72000 |
snapshot_too_old |
| Class F0 — Configuration File Error | |
F0000 |
config_file_error |
F0001 |
lock_file_exists |
| Class HV — Foreign Data Wrapper Error (SQL/MED) | |
HV000 |
fdw_error |
HV005 |
fdw_column_name_not_found |
HV002 |
fdw_dynamic_parameter_value_needed |
HV010 |
fdw_function_sequence_error |
HV021 |
fdw_inconsistent_descriptor_information |
HV024 |
fdw_invalid_attribute_value |
HV007 |
fdw_invalid_column_name |
HV008 |
fdw_invalid_column_number |
HV004 |
fdw_invalid_data_type |
HV006 |
fdw_invalid_data_type_descriptors |
HV091 |
fdw_invalid_descriptor_field_identifier |
HV00B |
fdw_invalid_handle |
HV00C |
fdw_invalid_option_index |
HV00D |
fdw_invalid_option_name |
HV090 |
fdw_invalid_string_length_or_buffer_length |
HV00A |
fdw_invalid_string_format |
HV009 |
fdw_invalid_use_of_null_pointer |
HV014 |
fdw_too_many_handles |
HV001 |
fdw_out_of_memory |
HV00P |
fdw_no_schemas |
HV00J |
fdw_option_name_not_found |
HV00K |
fdw_reply_handle |
HV00Q |
fdw_schema_not_found |
HV00R |
fdw_table_not_found |
HV00L |
fdw_unable_to_create_execution |
HV00M |
fdw_unable_to_create_reply |
HV00N |
fdw_unable_to_establish_connection |
| Class P0 — PL/pgSQL Error | |
P0000 |
plpgsql_error |
P0001 |
raise_exception |
P0002 |
no_data_found |
P0003 |
too_many_rows |
P0004 |
assert_failure |
| Class XX — Internal Error | |
XX000 |
internal_error |
XX001 |
data_corrupted |
XX002 |
index_corrupted |