Смущен странным поведением импорта в хранимых процедурах plpython

Я использую Postgresql 9.4, и я не понимаю, как он обрабатывает импорт plpython, особенно в отношении Decimal.

Первый пример:

CREATE OR REPLACE FUNCTION devel.foo()
  RETURNS void AS
$BODY$
def myfoo():
    eval("Decimal('40.0')")

myfoo()
plpy.notice("done")
$BODY$
  LANGUAGE plpythonu VOLATILE
  COST 100;
ALTER FUNCTION devel.foo()
  OWNER TO postgres;

SELECT devel.foo();

Результат:

ОШИБКА: NameError: имя «Десятичное число» не определено

Немного удивительно, поскольку одно из изменений в Postgresql 9.4 заключается в том, что предполагается неявное использование Decimal вместо float внутри plpython. Но хорошо, давайте добавим импорт:

CREATE OR REPLACE FUNCTION devel.foo()
  RETURNS void AS
$BODY$
from decimal import Decimal

def myfoo():
    eval("Decimal('40.0')");

myfoo()
plpy.notice("done")
$BODY$
  LANGUAGE plpythonu VOLATILE
  COST 100;
ALTER FUNCTION devel.foo()
  OWNER TO postgres;

select devel.foo()

Опять таки:

ОШИБКА: NameError: имя «Десятичное число» не определено

Теперь попробуем разместить импорт внутри функции:

CREATE OR REPLACE FUNCTION devel.foo()
  RETURNS void AS
$BODY$
def myfoo():
    from decimal import Decimal
    eval("Decimal('40.0')")

myfoo()
plpy.notice("done")
$BODY$
  LANGUAGE plpythonu VOLATILE
  COST 100;
ALTER FUNCTION devel.foo()
  OWNER TO postgres;


select devel.foo();

Результат: devel.foo() теперь выполняется без проблем, печатая 'done'. Еще одна любопытная вещь: также работает следующее:

CREATE OR REPLACE FUNCTION devel.foo()
  RETURNS void AS
$BODY$
from decimal import Decimal
def myfoo():
    var = Decimal('40.0')
    eval("Decimal('40.0')")

myfoo()
plpy.notice("done")
$BODY$
  LANGUAGE plpythonu VOLATILE
  COST 100;
ALTER FUNCTION devel.foo()
  OWNER TO postgres;

Вопросы: почему импорт верхнего уровня не виден внутри myfoo()? Почему это вдруг работает, если я использую Decimal внутри функции?


person Primordial_Hamster    schedule 20.01.2021    source источник


Ответы (1)


Это поведение Python описано здесь. Среда выполнения plpythonu не является глобальной.

Обратите внимание, что eval() не имеет доступа к вложенным областям (нелокальным) во внешней среде.

Попробуйте этот код в python (вне PostgreSQL):

def bar():
    from decimal import Decimal
    def foo():
        return eval("Decimal('40.0')")

    return foo()

print(bar())

Это не удается с NameError: name 'Decimal' is not defined.

Помещение Decimal в ваши глобальные переменные с помощью global работает:

def bar():
    from decimal import Decimal
    global Decimal
    def foo():
        return eval("Decimal('40.0')")

    return foo()

print(bar())
person Mike Organek    schedule 20.01.2021