Подтвердить что ты не робот

Обработка исключений PostgreSQL

Я новичок в PostgreSQL. Может кто-нибудь исправить этот запрос.

BEGIN TRANSACTION;

BEGIN;
    CREATE TABLE "Logs"."Events"
    (
        EventId BIGSERIAL NOT NULL PRIMARY KEY,
        PrimaryKeyId bigint NOT NULL,
        EventDateTime date NOT NULL DEFAULT(now()),
        Action varchar(12) NOT NULL,
        UserId integer NOT NULL REFERENCES "Office"."Users"(UserId),
        PrincipalUserId varchar(50) NOT NULL DEFAULT(user)
    );

    CREATE TABLE "Logs"."EventDetails"
    (
        EventDetailId BIGSERIAL NOT NULL PRIMARY KEY,
        EventId bigint NOT NULL REFERENCES "Logs"."Events"(EventId),
        Resource varchar(64) NOT NULL,
        OldVal varchar(4000) NOT NULL,
        NewVal varchar(4000) NOT NULL
    );


    COMMIT TRANSACTION;
    RAISE NOTICE 'Task completed sucessfully.'
EXCEPTION;
    ROLLBACK TRANSACTION;
    RAISE ERROR @ErrorMessage, @LineNumber, @ErrorState --how to catch errors and display them????
END;

Вопросы:

  • Как напечатать сообщение типа "PRINT" в T-SQL?
  • Как поднять ошибки с информацией об исключениях?
4b9b3361

Ответ 1

Чтобы поймать сообщение об ошибке и его код:

do $$       
begin

    create table yyy(a int);
    create table yyy(a int); -- this will cause an error

exception when others then 

    raise notice 'The transaction is in an uncommittable state. '
                 'Transaction was rolled back';

    raise notice '% %', SQLERRM, SQLSTATE;

end; $$ 
language 'plpgsql';

Еще не нашли номер строки

ОБНОВЛЕНИЕ 16 апреля 2019

В соответствии с предложением Диего Скаравагги, для Postgres 9.2 и выше используйте GET STACKED DIAGNOSTICS:

do language plpgsql $$
declare
    v_state   TEXT;
    v_msg     TEXT;
    v_detail  TEXT;
    v_hint    TEXT;
    v_context TEXT;
begin

    create table yyy(a int);
    create table yyy(a int); -- this will cause an error

exception when others then 

    get stacked diagnostics
        v_state   = returned_sqlstate,
        v_msg     = message_text,
        v_detail  = pg_exception_detail,
        v_hint    = pg_exception_hint,
        v_context = pg_exception_context;

    raise notice E'Got exception:
        state  : %
        message: %
        detail : %
        hint   : %
        context: %', v_state, v_msg, v_detail, v_hint, v_context;

    raise notice E'Got exception:
        SQLSTATE: % 
        SQLERRM: %', SQLSTATE, SQLERRM;     

    raise notice '%', message_text; -- invalid. message_text is contextual to GET STACKED DIAGNOSTICS only

end; $$;

Результат:

NOTICE:  Got exception:
        state  : 42P07
        message: relation "yyy" already exists
        detail : 
        hint   : 
        context: SQL statement "create table yyy(a int)"
PL/pgSQL function inline_code_block line 11 at SQL statement
NOTICE:  Got exception:
        SQLSTATE: 42P07 
        SQLERRM: relation "yyy" already exists

ERROR:  column "message_text" does not exist
LINE 1: SELECT message_text
               ^
QUERY:  SELECT message_text
CONTEXT:  PL/pgSQL function inline_code_block line 33 at RAISE
SQL state: 42703

Помимо GET STACKED DIAGNOSTICS совместима со стандартом SQL, ее диагностические переменные (например, message_text) являются контекстными только для GSD. Так что, если у вас есть поле с именем message_text в вашей таблице, нет никаких шансов, что GSD может помешать значению вашего поля.

Все еще нет номера строки, хотя.

Ответ 2

Используйте оператор DO, новый вариант в версии 9.0:

DO LANGUAGE plpgsql
$$
BEGIN
CREATE TABLE "Logs"."Events"
    (
        EventId BIGSERIAL NOT NULL PRIMARY KEY,
        PrimaryKeyId bigint NOT NULL,
        EventDateTime date NOT NULL DEFAULT(now()),
        Action varchar(12) NOT NULL,
        UserId integer NOT NULL REFERENCES "Office"."Users"(UserId),
        PrincipalUserId varchar(50) NOT NULL DEFAULT(user)
    );

    CREATE TABLE "Logs"."EventDetails"
    (
        EventDetailId BIGSERIAL NOT NULL PRIMARY KEY,
        EventId bigint NOT NULL REFERENCES "Logs"."Events"(EventId),
        Resource varchar(64) NOT NULL,
        OldVal varchar(4000) NOT NULL,
        NewVal varchar(4000) NOT NULL
    );

    RAISE NOTICE 'Task completed sucessfully.';    
END;
$$;

Ответ 3

Вы можете написать это как psql script, например,

START TRANSACTION;
CREATE TABLE ...
CREATE TABLE ...
COMMIT;
\echo 'Task completed sucessfully.'

и запустите

psql -f somefile.sql

Повышение ошибок с параметрами невозможно в PostgreSQL напрямую. При портировании такого кода некоторые люди кодируют необходимую информацию в строке ошибки и анализируют ее при необходимости.

Все работает немного по-другому, поэтому будьте готовы переучиться/переосмыслить/переписать много вещей.

Ответ 4

Просто хочу добавить мои два цента в этот старый пост:

На мой взгляд, почти все механизмы реляционных баз данных включают выполнение транзакции фиксации автоматически после выполнения команды DDL, даже если у вас есть autocommit = false, поэтому вам не нужно запускать транзакцию, чтобы избежать создания потенциального усеченного объекта, поскольку он полностью ненужным.