Успешно сохраненная ХП выдает Malformed string, пока не recompile. Why ?

Таблоид
Дата: 23.04.2012 21:18:53
hi all

Предыстория.
Есть достаточно сложный .sh, извлекающий из продакшена DDL всех ХП, триггеров, вьюх, доменов, исключений, генераторов, ролей, привилегий и комментариев к полям таблиц и параметрам ХП.
Извлечение метаданных производится с пом. нескольких ХП (sys_get_xxxx_ddl, где "xxxx" = proc, view, trig, exce, doma, priv и т.д.), которые живут в продакшене и лезут в словарь базы.

В итоге, создаются .sql-файлы, которые содержат тела-пустышки программных объектов, а также (другие) .sql-файлы с заполненными телами.
Далее эти файлы в строго опр. порядке натравливаются на базу назначения (репликант-базу).
Текст процедур sys_get_xxx_ddl также переносятся в базу назначения, как и всех прочих. Никаких ошибок при этом не возникает (отслеживаю размер лога ошибок - он нулевой).

Вопрос: может ли такое быть, что одна из "словарно-парсинговых" процедур (sys_get_xxx_ddl), создаваемых в базе назначения как и все прочие, оказывается в каком-то странном состоянии: пока её не перекомпилишь заново, она вместо результата возвращает ошибку 'malformed string' ?

Вот её текст:
create or alter procedure SYS_GET_EXCE_DDL
returns (
    SRC varchar(8192))
AS
begin
-- Extract DDL of EXCEPTIONS to be executed as statements in isql
-- Encloses 'create exception' in execute statement and begin..end blocks
-- to prevent errors during creation (no `recreate` exсeption present now in FB)
for
with
    e as(
        select xe.rdb$exception_name ex_name, xe.rdb$message ex_msg
        from rdb$exceptions xe
        where coalesce(xe.rdb$system_flag,0)=0 -- do NOT: is distinct from 1
    )
    select
        case x.rt
        when -30 then 'set term ^;'
        when -20 then 'execute block as begin'
        when -10 then 'if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = '''||upper(trim(ex_name))||''')) then'
        when 0 then
            '  execute statement '''
                ||'create exception '||trim(ex_name)||' '||''''''
                ||coalesce(replace(ex_msg,'''',''''''),'')||''''''
            ||';'';'
         when 10 then 'when any do begin end'
         when 20 then 'end^'
         when 30 then 'set term ;^'
         end   src
    from e
    join (select -30 rt from rdb$database
          union all select -20 from rdb$database
          union all select -10 from rdb$database
          union all select   0 from rdb$database
          union all select  10 from rdb$database
          union all select  20 from rdb$database
          union all select  30 from rdb$database
         ) x on 1=1
    order by ex_name,rt
into src
do suspend;
end

Результат выборки из этой ХП на продакшене (при нормальном исходе) должен быть таким:
+
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'A_FOR_BREAK')) then
execute statement 'create exception A_FOR_BREAK ''Остановить FOR SELECT'';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'DEL_ROLLBACK')) then
execute statement 'create exception DEL_ROLLBACK ''Удалить запись нельзя, есть ссылки в других таблицах.'';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'ERROR_VALUTE')) then
execute statement 'create exception ERROR_VALUTE ''ВНИМАНИЕ! Нельзя изменить/добавить/удалить курс валюты.'';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'USER_EXCEPTION')) then
execute statement 'create exception USER_EXCEPTION '''';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'VAR_EXCEPTION')) then
execute statement 'create exception VAR_EXCEPTION ''VARIABLE EXCEPTION'';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'XA_FOR_BREAKE')) then
execute statement 'create exception XA_FOR_BREAKE ''ERROR!'';';
when any do begin end
end^
set term ;^
set term ^;
execute block as begin
if (not exists(select * from rdb$exceptions ex where ex.rdb$exception_name = 'XERR_NO_PERIOD')) then
execute statement 'create exception XERR_NO_PERIOD ''ERROR!'';';
when any do begin end
end^
set term ;^

Однако, если делать её вызов из консоли с чарсетом UTF8 и соотв-щим указанием его в -ch ключе:
isql kprobe4upd.fdb -ch utf8 -i get_exc.sql -m | sed -e "s/ \{1,\}$//" >  get_exc2.txt
-- то получаем облом на первом же execute statement 'create exception A_FOR_BREAK ''Остановить FOR SELECT'';'; -
Statement failed, SQLSTATE = 42000
Malformed string
-At procedure 'SYS_GET_EXCE_DDL' line: 45, col: 1
After line 1 in file get_exc.sql

В итоге, при переводе базы назначения в "боевой режим" приходится открывать эту ХП в ИБЭ и перекомпилять заново.
Отчего такое может быть ?
Таблоид
Дата: 23.04.2012 21:56:45
PS. Методом тыка выяснил, что пересоздание только этой ХП, выполняемое через isql (т.е. скармливание ему скрипта drop sys_get_...; commit; и далее create or alter с телом этой процедуры), *не* помогает.
Надо обязательно открывать её в ИБЭ и уже там жамкать Ctrl-F9.
Чудеса какие-то... Чем компиляция процедуры в ИБЭ оказалась "лучше" её пересоздания путём натравки isql на соотв-щий скрипт ??
Dimitry Sibiryakov
Дата: 23.04.2012 22:04:51

Правильным чарсетом коннекта скорее всего.

Posted via ActualForum NNTP Server 1.5

Таблоид
Дата: 23.04.2012 22:06:53
Dimitry Sibiryakov,

он уже сто лет как правильный: в консоли PUTTy выполняю isql -ch utf8 ...
И потом: почем именно ЭТА, ЕДИНСТВЕННАЯ процедура, отказывается выдавать результат ?
Таблоид
Дата: 23.04.2012 22:11:57
насторожило вот что: в таблице rdb$exceptions поле rdb$exception_name имеет чарсет UNICODE_FSS, а поле rdb$message - чарсет NONE. Может ли это как-то влиять ?
Таблоид
Дата: 23.04.2012 22:35:54
Нарыл тут кое-что.
recreate table ttt_none(msg varchar(60) character set none);
commit;
insert into ttt_none values('Съешь ещё этих мягких французских булок да выпей чаю');
commit;

recreate table ttt_defa(msg varchar(60));
commit;
insert into ttt_defa values('Съешь ещё этих мягких французских булок да выпей чаю');
commit;
Далее:
SQL> select cast(msg as varchar(60) character set utf8) msg from ttt_none;

MSG
===============================================================================
Statement failed, SQLSTATE = 22000
Malformed string
SQL> select cast(msg as varchar(60) character set utf8) msg from ttt_defa;

MSG
===============================================================================
Съешь ещё этих мягких французских булок да выпей чаю

В то же время:
SQL> select cast(msg as varchar(60) character set win1251) msg from ttt_none;

MSG
===============================================================================
Съешь ещё этих мягких французских булок да выпей чаю

То есть, если поле создано с чарсетом = NONE, то его нельзя приводить к UTF8. Но можно почему-то к win1251.
Хм... Странно таки!
Dimitry Sibiryakov
Дата: 23.04.2012 22:53:21

Таблоид
Может ли это как-то влиять ?

Да. Занеси это в трекер. У rdb$message тоже должен быть чарсет unicode_fss.

Posted via ActualForum NNTP Server 1.5

Таблоид
Дата: 23.04.2012 23:00:53
а зачем вообще этот UNICODE_FSS юзается в словарных таблицах до сих пор ? почему не UTF8 ?
Ведь вот же, сами пишут:
doc/readme.intl.txt
UTF8 character set
------------------

The UNICODE_FSS character set has a number of problems: it's an old version of UTF8, accepts
malformed strings and doesn't enforce correct maximum string length. In FB 1.5.X UTF8 it's an
alias to UNICODE_FSS.
Now UTF8 is a new character set, without these problems of UNICODE_FSS.

PS. И почему, кстати, в intl/fbintl.conf вообще нет упоминания UTF8, хотя в базе он как-то присуствует ?
dimitr
Дата: 24.04.2012 07:51:56
Таблоид
а зачем вообще этот UNICODE_FSS юзается в словарных таблицах до сих пор ? почему не UTF8 ?

это изменение ОДС и проблемы обратной совместимости. Даже в 3.0 у Адриано возникли с этим проблемы.

Таблоид
почему, кстати, в intl/fbintl.conf вообще нет упоминания UTF8, хотя в базе он как-то присуствует ?

там регистрируются только внешние чарсеты/коллейты. OCTETS/ASCII/UTF8 - внутренние, т.е. реализованы самим сервером.
Таблоид
Дата: 24.04.2012 07:56:26
А в трекер-то заносить (про rdb$exceptions.rdb$message в чарсете NONE) ?