Функция не вызывается внутри цикла for-in в PostgreSQL

#postgresql #random

Вопрос:

У меня есть этот код, который предназначен для создания функции Lorem Ipsum для заполнения данных в хранилище данных. Я намеренно сократил количество слов для простоты.

 create or replace function lipsum_word (
    _useInitCap boolean default false
)
returns varchar
immutable language plpgsql as $
declare
    _word varchar;
    _words constant varchar[] = '{"abbas", "abbatia", "abbatis", "abduco", "abeo", "volatilis:", "volens", "volo", "volpes", "voltur", "volturius", "volubilis", "volubiliter", "voluntarius", "voluntas", "volup", "vulticulus", "vultuosus", "vultur", "vulturius", "vultus", "vulva", "werumensium", "winged", "wreck", "xiphias"}';
begin
    _word = _words[random() * array_length(_words, 1)];

    if _useInitCap then 
        _word = initcap(_word);
    end if;

    return _word;
end
$;

create or replace function lipsum_sentence (
    _wordCount int default 5,
    _useInitCap boolean default false,
    _useFinalDot boolean default true
)
returns varchar
immutable language plpgsql as $
declare
    _sentence varchar;
begin
    _sentence = lipsum_word();
    if _useInitCap then 
        _sentence = initcap(_sentence);
    end if;

    for i in 1 .. _wordCount - 1 loop
        _sentence = _sentence || ' ' || lipsum_word();
    end loop;

    if _useFinalDot then
        _sentence = _sentence || '.';
    end if;

    return _sentence;
end
$;
 

Код действителен, но lipsum_word() вызов внутри for-in цикла работает неправильно, так как возвращаемые значения примерно такие

 select lipsum_word();
select lipsum_word('true');
select lipsum_sentence();
select lipsum_sentence(8, false, true);

 lipsum_word 
-------------
 volturius
(1 row)

 lipsum_word 
-------------
 Vultus
(1 row)

 lipsum_word 
-------------
 abbatis
(1 row)

             lipsum_sentence              
------------------------------------------
 werumensium winged winged winged winged.
(1 row)

                               lipsum_sentence                               
-----------------------------------------------------------------------------
 werumensium winged winged winged winged winged winged winged winged winged.
(1 row)

                    lipsum_sentence                     
--------------------------------------------------------
 Werumensium winged winged winged winged winged winged.
(1 row)

                        lipsum_sentence                        
---------------------------------------------------------------
 werumensium winged winged winged winged winged winged winged.
(1 row)

slim:watson-dw-v1.0.15 coterobarros$ ./runlipsum 
Password for user postgres: 
 lipsum_word 
-------------
 abbatia
(1 row)

 lipsum_word 
-------------
 Vulturius
(1 row)

                     lipsum_sentence                     
---------------------------------------------------------
 winged voluntarius voluntarius voluntarius voluntarius.
(1 row)

                                       lipsum_sentence                                       
---------------------------------------------------------------------------------------------
 winged voluntarius voluntarius voluntarius voluntarius voluntarius voluntarius voluntarius.
(1 row)
 

Что касается запасных вызовов lorem_word() , то функция работает нормально. Я также протестировал raise notice 'random' внутреннюю lorem_word функцию, чтобы узнать, что цикл не вызывает ее только один раз.

Есть идеи по поводу такого поведения?

Ответ №1:

Объявите lipsum_word как VOLATILE вместо IMMUTABLE . Смотрите документы о волатильности функций; ни STABLE то, ни IMMUTABLE другое не будет работать для функций, которые могут законно возвращать разные результаты для одинаковых вызовов.

Комментарии:

1. Поэтому, что интересно, для неизменяемой функции компилятор избегает ее вызова и сохраняет последнее значение для идентичного набора параметров.