Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / сообщение об ошибке на клиенте / 9 сообщений из 9, страница 1 из 1
25.06.2008, 10:55
    #35392799
nikolaich2
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
Есть такая процедура:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE OR REPLACE FUNCTION InsTable1 (f1 varchar( 50 ), f2 varchar( 250 )) RETURNS int AS $$ 
	BEGIN
		IF ($ 1  ISNULL) OR ($ 2  ISNULL) THEN
			 ;
		END IF;
		INSERT INTO Table1 (field1, field2)
		VALUES ($ 1 , $ 2 );
		RETURN  0 ;
        EXCEPTION
		WHEN OTHERS THEN
		  1 ;
	END;
$$ LANGUAGE plpgsql;

Как получить текстовое сообщение, которое генерирует RAISE EXCEPTION ' ' на клиенте.
У меня получается только обрабатывать RETURN.
И еще это сообщение в pgAdmin я тоже не вижу.

Клиент на Delphi.
PostgreSQL 8.3.0.
...
Рейтинг: 0 / 0
25.06.2008, 12:39
    #35393227
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
Во-первых, я что-то не увидел в процедуре raise exception. У вас ошибка просто подавляется и всё.
Во-вторых, если raise таки будет, то в дельфях наверняка (я просто уже не помню, на джаве точно) будет возникать какой-нибудь sql-exception, отловив который можно покопаться во внутренностях и найти сообщение.
...
Рейтинг: 0 / 0
25.06.2008, 12:54
    #35393298
nikolaich2
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
pamirВо-первых, я что-то не увидел в процедуре raise exception. У вас ошибка просто подавляется и всё.
Во-вторых, если raise таки будет, то в дельфях наверняка (я просто уже не помню, на джаве точно) будет возникать какой-нибудь sql-exception, отловив который можно покопаться во внутренностях и найти сообщение.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE OR REPLACE FUNCTION InsTable1 (f1 varchar( 50 ), f2 varchar( 250 )) RETURNS int AS $$ 
	BEGIN
		IF ($ 1  ISNULL) OR ($ 2  ISNULL) THEN
		RAISE  EXCEPTION 'Отсутсвуют входные параметры!!!!' ;
		END IF;
		INSERT INTO Table1 (field1, field2)
		VALUES ($ 1 , $ 2 );
		RETURN  0 ;
        EXCEPTION
		WHEN OTHERS THEN
		  1 ;
	END;
$$ LANGUAGE plpgsql;

Прошу прощения. Вот исправленная функция.

Я тоже надеялся, что будет до клиента долетать exception. И в MS SQL 2000 у меня все работало. А тут не получается. Возможно нужны особые настройки для сервера?
...
Рейтинг: 0 / 0
25.06.2008, 13:01
    #35393326
nikolaich2
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
Самая "правильная" версия функции:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE OR REPLACE FUNCTION InsTable1 (f1 varchar( 50 ), f2 varchar( 250 )) RETURNS int AS $$ 
	BEGIN
		IF ($ 1  ISNULL) OR ($ 2  ISNULL) THEN
		RAISE  EXCEPTION 'Отсутсвуют входные параметры!!!!' ;
		END IF;
		INSERT INTO Table1 (field1, field2)
		VALUES ($ 1 , $ 2 );
		RETURN  0 ;
        EXCEPTION
		WHEN OTHERS THEN
		 RETURN  1 ;
	END;
$$ LANGUAGE plpgsql;

В pgAdmin также не вылетает exception, хотя возвращает 1, что свидетельствует о том, что исключение генерируется и обрабатывается.
...
Рейтинг: 0 / 0
25.06.2008, 13:01
    #35393327
Гость_0
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
по моему Вы сам и перехватываете исключение которое сгенерировали, тем самым подавляя сообщение об ошибке
...
Рейтинг: 0 / 0
25.06.2008, 13:12
    #35393374
nikolaich2
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
Гость_0по моему Вы сам и перехватываете исключение которое сгенерировали, тем самым подавляя сообщение об ошибке

Да. Так оно и есть. Спасибо за подсказку.
...
Рейтинг: 0 / 0
25.06.2008, 13:17
    #35393393
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
nikolaich2 Гость_0по моему Вы сам и перехватываете исключение которое сгенерировали, тем самым подавляя сообщение об ошибке

Да. Так оно и есть. Спасибо за подсказку.Во-во. Вы ж его генерите и тут же перехватываете.
...
Рейтинг: 0 / 0
25.06.2008, 13:32
    #35393453
nikolaich2
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
pamir nikolaich2 Гость_0по моему Вы сам и перехватываете исключение которое сгенерировали, тем самым подавляя сообщение об ошибке

Да. Так оно и есть. Спасибо за подсказку.Во-во. Вы ж его генерите и тут же перехватываете.

В MS SQL 2000 возможности обрабатывать exception в процедуре нет, а в PostgreSQL я только начал разбираться. Вот и получилась такая ерунда.

И еще. Может посоветуете приличный способ передать информацию об ошибке на клиент. Я пока вижу следующие:
- просто ловить exception на клиенте и потом с ним разбираться (но мне кажется, что будет не правильно не обработать исключение на сервере если есть такая возможность):
- обрабатывать исключение на сервере, а на клиенте передавать соответсвующие коды или тексты через RETURNS.
...
Рейтинг: 0 / 0
25.06.2008, 14:10
    #35393610
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
сообщение об ошибке на клиенте
Поскольку зарайзить случившуюся и перехваченную ошибку нельзя (т.е. поймать её, что-то сделать и поднять дальше) я написал небольшую систему обработки ошибки. Гуру подскажут, может быть я был не прав, но мне понравилось (правда, система так и осталась в тестовом варианте. Это была самописка для одной игры, которая в итоге не понадобилась).
Итак. На стороне БД 3 функции.
1. Собственно, делает raise exception, но пихает туда нужную мне информацию.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
CREATE OR REPLACE FUNCTION err_raise_exception(i_sqlstate text, i_sqlerrm text, i_fnc_name text)
  RETURNS void AS
$BODY$declare
  l_err_str text;
begin
  raise notice 'in err_raise_exception sqlstate=%, sqlerrm=%, fnc_name=%',i_sqlstate, i_sqlerrm, i_fnc_name;
  if (i_sqlstate='P0001') then
    l_err_str=i_sqlerrm;
  else
    l_err_str=err_encode_exception('PG.'||i_sqlstate||'.'||i_fnc_name, i_sqlerrm);
  end if;
  raise notice 'finish err_raise_exception l_err_str=%',l_err_str;
  raise exception '%', l_err_str;
end;$BODY$
  LANGUAGE 'plpgsql' STABLE;

2. Кодирует нужную информацию - вызывается внутри первой

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
CREATE OR REPLACE FUNCTION err_encode_exception(i_sqlstate text, i_sqlerrm text)
  RETURNS text AS
$BODY$declare
  l_ret text;
begin
  raise notice 'in err_encode_exception sqlstate=%, sqlerrm=%',i_sqlstate, i_sqlerrm;
  l_ret='<'||i_sqlstate||'=#='||i_sqlerrm||'>';
  raise notice 'finish err_encode_exception return=%',l_ret;
  return l_ret;
end;$BODY$
  LANGUAGE 'plpgsql' STABLE;

3. Декодирует информацию. Используется, если для перехвата и разбора эксепшена на стороне сервера.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
CREATE OR REPLACE FUNCTION err_decode_exception(i_error_string text)
  RETURNS err_exception AS
$BODY$declare
  l_ret err_exception;
begin
  raise notice 'in err_decode_exception i_error_string=%',i_error_string;
  if (i_error_string ~ '^\<.+=#=.*\>$') then
    l_ret.sqlstate=substring(i_error_string,'^\<(.+)=#=.*\>$');
    l_ret.sqlerrm=substring(i_error_string,'^\<.+=#=(.*)\>$');
  else
    l_ret.sqlstate='unknown';
    l_ret.sqlerrm=i_error_string;
  end if;
  raise notice 'finish err_decode_exception l_ret.sqlstate=%, l_ret.sqlerrm=%',l_ret.sqlstate,l_ret.sqlerrm;
  return l_ret;
end;$BODY$
  LANGUAGE 'plpgsql' STABLE;

Ну и создал собственный тип
Код: plaintext
1.
2.
CREATE TYPE err_exception AS
   (sqlstate text,
    sqlerrm text);

В любой серверной функции ошибки перехватываются (внутренние) либо, если это ошибка, которую мне надо сгенерить - генерится следующим образом. В каждой функции есть глобальная секция exception
when others then
В которой вызывается моя процедура (см. пример ниже). Если внутри функции нужно сгенерировать мою ошибку с моим текстом, я это делаю. Она перехватится глобальной секцией и поднимется уже кодированная. Пример:


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
CREATE OR REPLACE FUNCTION process_coach(i_uid numeric, i_operation character varying, i_code team_code, i_coach_id integer)
  RETURNS void AS
$BODY$declare
  CMD_ADD    character varying( 6 )= 'SET';
  CMD_DEL    character varying( 6 )= 'REMOVE';

  l_oper character varying( 20 );
  l_tmp numeric;
  l_assoc_code assoc_code;
begin
  raise notice 'in process_coach uid=%, operation=%, code=%, coach_id=%',i_uid, i_operation, i_code, i_coach_id;

  l_oper = upper(i_operation);
  -------- (+)Блок генерации ошибок --------
  if (i_uid is null or i_uid< 1 ) then
    raise exception '%',err_encode_exception('process_coach.uid_is_empty', 'Пользователь не определен');
  end if;

  if (l_oper!=CMD_ADD and l_oper!=CMD_DEL) then
    raise exception '%',err_encode_exception('process_coach.bad_operation', 'Операция ('||l_oper||') не распознана');
  end if;

  if (i_code is null or i_code='') then
    raise exception '%',err_encode_exception('process_coach.bad_team_code', 'Код команды не может быть пустым');
  else
    if (is_team_exists(i_code)= 0 ) then
      raise exception '%',err_encode_exception('process_coach.team_not_exists', 'Команды ('||i_code||') не существует');
    end if;
  end if;

  l_assoc_code=get_assoc_code_for_team(i_code);
  if (l_oper=CMD_ADD) then
    if (i_coach_id is null or i_coach_id <  0 ) then
      raise exception '%',err_encode_exception('process_coach.bad_coach_id', 'Не передан или пустой ID назначаемого тренера');
    end if;
  elsif (l_oper = CMD_DEL) then
  end if;

  -------------- проверить права пользователя!!!!!!!!!!!!!!!! ----------------
  l_tmp = is_assoc_cheif(l_assoc_code, i_uid, true, true, true);
  if (l_tmp =  0 ) then
    raise exception '%',err_encode_exception('process_coach.have_not_rights', 'Вы не можете оперировать командами в этой ('||l_assoc_code||') федерации (ассоциации)');
  end if;

  -------- (-)Блок генерации ошибок --------
//тут ещё огромный кусок функции, которая что-то делает, где могут возникать ошибки или генериться руками, как сделано выше.

  raise notice 'finish process_coach';
//а это и есть глобальная секция перехвата. В неё попадет любая ошибка в этой функции.
exception
  when others then
    select err_raise_exception(SQLSTATE,SQLERRM,'process_coach');
end;$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

Таким образом, нужно поддерживать соглашение о структуре ЛЮБОЙ функции на стороне сервера - наличие глобального перехватчика в каждой функции.
Как можно видеть, у меня присутствуют собственные стринговые коды ошибок. Если же ошибка постгреса (типа констрейнтов и т.п.) то они ловятся так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
    begin
      insert into user_teams (team_code, user_id) values (i_code, i_coach_id);
    exception
      when UNIQUE_VIOLATION THEN
        raise notice 'in process_coach unique violation %', SQLERRM;
        if (position('pk_user_teams' in SQLERRM)> 0 ) then
          raise exception '%',err_encode_exception('process_coach.same_coach_team', 'Игрок уже является тренером этой же команды');
        end if;
        if (position('user_teams_team_unique' in SQLERRM)> 0 ) then
          raise exception '%',err_encode_exception('process_coach.team_not_unique', 'У этой ('||i_code||') команды уже есть тренер');
        end if;
        select err_raise_exception(SQLSTATE,SQLERRM,'process_coach');
      when foreign_key_violation then
        raise notice 'in process_coach foreign_key_violation %', SQLERRM;
        if (position('fk_user_teams_team_code' in SQLERRM)> 0 ) then
          raise exception '%',err_encode_exception('process_coach.no_such_team', 'Такой ('||i_code||') команды не существует');
        end if;
        if (position('fk_user_teams_userid' in SQLERRM)> 0 ) then
          raise exception '%',err_encode_exception('process_coach.no_such_user', 'Такой ('||i_coach_id||') пользователь в системе не зарегистрирован');
        end if;
        select err_raise_exception(SQLSTATE,SQLERRM,'process_coach');
    end;    
Т.е. я в тексте ошибки ищу имя ключа (внешнего или уникального) и генерю соответствующую ошибку.

И теперь, что на клиенте (правда, на джаве)

Код: plaintext
1.
2.
3.
4.
5.
6.
        } catch (SQLException ex) {
        //тут ещё роллбэк, но я этот кусок опущу

            ok = false;
            FpPgError err = new FpPgError(ex);
            lRet = lRet + err.getErrMsg();

FpPgError - это класс, конструктор которого получает эксепшн. Парсит его текст, как это делает функция err_encode_exception, только на клиенте.
Дальше я могу делать с ним что угодно - в нем содержится МОЙ код ошибки, МОЙ текст, который я могу показать пользователю.

Проблема: я не стал заморачиваться на мультиязычность. По хорошему, надо расширить эту систему - по коду ошибки искать сообщение на языке пользователя. А по умолчанию, например, писать на русском (в принципе, можно сказать, что сейчас у меня всё работает "по умолчанию").

Да! Текст ошибки получается таким:
<код_моей_ошибки=#=текст моей ошибки>
либо, если это необработанная постгресовая ошибка (уникальность, которую я не поймал или ещё что-то)
<PG.код_ошибки_постгреса.имя_функции_где_произошла=#=sqlerrm>
sqlerrm - это постгресовый текст ошибки. Как он её выдает.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / сообщение об ошибке на клиенте / 9 сообщений из 9, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]