48.3. Значения данных
Вообще говоря, цель исполнителя PL/Python — обеспечить «естественное» соответствие между мирами PostgreSQL и Python. Этим объясняется выбор правил сопоставления данных, описанных ниже.
48.3.1. Сопоставление типов данных
Когда вызывается функция PL/Python, её аргументы преобразуются из типа PostgreSQL в соответствующий тип Python по таким правилам:
Тип PostgreSQL
boolean
преобразуется в типbool
языка Python.Типы PostgreSQL
smallint
иint
преобразуются в типint
языка Python. Типы PostgreSQLbigint
иoid
становятся типамиlong
в Python 2 иint
в Python 3.Типы PostgreSQL
real
иdouble
преобразуются в типfloat
языка Python.Тип PostgreSQL
numeric
преобразуется в типDecimal
среды Python. Этот тип импортируется из пакетаcdecimal
, при его наличии. В противном случае используетсяdecimal.Decimal
из стандартной библиотеки. Типcdecimal
работает значительно быстрее, чемdecimal
. Однако в Python версии 3.3 и выше типcdecimal
включается в стандартную библиотеку под именемdecimal
, так что теперь этого различия нет.Тип PostgreSQL
bytea
становится типомstr
в Python 2 иbytes
в Python 3. В Python 2 такую строку следует воспринимать как последовательность байт без какой-либо определённой кодировки символов.Все другие типы данных, включая типы символьных строк PostgreSQL, преобразуются в тип
str
языка Python. В Python 2 эта строка будет передаваться в кодировке сервера Postgres Pro; в Python 3 это будет строка в Unicode, как и все строки.Информация о нескалярных типах данных приведена ниже.
При завершении функции PL/Python её значение результата преобразуется в тип данных, объявленный как тип результата в PostgreSQL, следующим образом:
Когда тип результата функции в PostgreSQL —
boolean
, возвращаемое значение приводится к логическому типу по правилам, принятым в Python. То есть false будет возвращено для 0 и пустой строки, но, обратите внимание, для'f'
будет возвращено true.Когда тип результата функции PostgreSQL —
bytea
, возвращаемое значение будет преобразовано в строку (Python 2) или набор байт (Python 3), используя встроенные средства Python, а затем будет приведено к типуbytea
.Для всех других типов результата PostgreSQL возвращаемое значение преобразуется в строку с помощью встроенной в Python функции
str
, и полученная строка передаётся функции ввода типа данных PostgreSQL. (Если значение в Python имеет типfloat
, оно преобразуется встроенной функциейrepr
, а неstr
, для недопущения потери точности.)Из кода Python 2 строки должны передаваться в Postgres Pro в кодировке сервера Postgres Pro. При передаче строки, неприемлемой для текущей кодировки сервера, возникает ошибка, но не все несоответствия кодировки могут быть выявлены, так что с некорректной кодировкой всё же могут быть получены нечитаемые строки. Строки Unicode переводятся в нужную кодировку автоматически, так что использовать их может быть безопаснее и удобнее. В Python 3 все строки имеют кодировку Unicode.
Информация о нескалярных типах данных приведена ниже.
Заметьте, что логические несоответствия между объявленным в PostgreSQL типом результата и типом фактически возвращаемого объекта Python игнорируются — значение преобразуется в любом случае.
48.3.2. Null, None
Если функции передаётся значение SQL NULL, в Python значением этого аргумента будет None
. Например, функция pymax
, определённая как показано в Раздел 48.2, возвратит неверный ответ, получив аргументы NULL. Мы могли бы добавить указание STRICT
в определение функции, чтобы Postgres Pro поступал немного разумнее: при передаче значения NULL функция вовсе не будет вызываться, будет сразу возвращён результат NULL. С другой стороны, мы могли бы проверить аргументы на NULL в теле функции:
CREATE FUNCTION pymax (a integer, b integer) RETURNS integer AS $$ if (a is None) or (b is None): return None if a > b: return a return b $$ LANGUAGE plpythonu;
Как показано выше, чтобы выдать из функции PL/Python значение SQL NULL, нужно вернуть значение None
. Это можно сделать и в строгой, и в нестрогой функции.
48.3.3. Массивы, списки
Значения массивов SQL передаются в PL/Python в виде списка Python. Чтобы вернуть значение массива SQL из функции PL/Python, возвратите список Python:
CREATE FUNCTION return_arr() RETURNS int[] AS $$ return [1, 2, 3, 4, 5] $$ LANGUAGE plpythonu; SELECT return_arr(); return_arr ------------- {1,2,3,4,5} (1 row)
Многомерные массивы передаются в PL/Python в виде вложенных списков Python. Например, двухмерный массив представляется как список списков. При передаче многомерного массива SQL из функции PL/Python необходимо, чтобы все внутренние списки на каждом уровне имели одинаковый размер. Например:
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$ plpy.info(x, type(x)) return x $$ LANGUAGE plpythonu; SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]); INFO: ([[1, 2, 3], [4, 5, 6]], <type 'list'>) test_type_conversion_array_int4 --------------------------------- {{1,2,3},{4,5,6}} (1 row)
Другие последовательности Python, например кортежи, тоже принимаются для обратной совместимости с PostgreSQL версии 9.6 и ниже (где многомерные массивы не поддерживались). Однако они всегда воспринимаются как одномерные массивы, чтобы не возникало неоднозначности с составными типами. По этой же причине когда в многомерном массиве используется составной тип, он должен представляться как кортеж, а не список.
Учтите, что в Python и строки являются последовательностями, что может давать неожиданные эффекты, хорошо знакомые тем, кто программирует на Python:
CREATE FUNCTION return_str_arr() RETURNS varchar[] AS $$ return "hello" $$ LANGUAGE plpythonu; SELECT return_str_arr(); return_str_arr ---------------- {h,e,l,l,o} (1 row)
48.3.4. Составные типы
Аргументы составного типа передаются функции в виде сопоставлений Python. Именами элементов сопоставления являются атрибуты составного типа. Если атрибут в переданной строке имеет значение NULL, он передаётся в сопоставлении значением None
. Пример работы с составным типом:
CREATE TABLE employee ( name text, salary integer, age integer ); CREATE FUNCTION overpaid (e employee) RETURNS boolean AS $$ if e["salary"] > 200000: return True if (e["age"] < 30) and (e["salary"] > 100000): return True return False $$ LANGUAGE plpythonu;
Возвратить составной тип или строку таблицы из функции Python можно несколькими способами. В следующих примерах предполагается, что у нас объявлен тип:
CREATE TYPE named_value AS ( name text, value integer );
Результат этого типа можно вернуть как:
- Последовательность (кортеж или список, но не множество, так как оно не индексируется)
В возвращаемых объектах последовательностей должно быть столько элементов, сколько полей в составном типе результата. Элемент с индексом 0 присваивается первому полю составного типа, с индексом 1 — второму и т. д. Например:
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return ( name, value ) # или альтернативный вариант, в виде списка: return [ name, value ] $$ LANGUAGE plpythonu;
Чтобы выдать SQL NULL для какого-нибудь столбца, вставьте в соответствующую позицию
None
.Когда возвращается массив составных значений, его нельзя представить в виде списка, так как невозможно однозначно определить, представляет ли список Python составной тип или ещё одну размерность массива.
- Сопоставление (словарь)
Значение столбца результата получается из сопоставления, в котором ключом является имя столбца. Например:
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return { "name": name, "value": value } $$ LANGUAGE plpythonu;
Любые дополнительные пары ключ/значение в словаре игнорируются, а отсутствие нужных ключей считается ошибкой. Чтобы выдать SQL NULL для какого-нибудь столбца, вставьте
None
с именем соответствующего столбца в качестве ключа.- Объект (любой объект с методом
__getattr__
) Объект передаётся аналогично сопоставлению. Пример:
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ class named_value: def __init__ (self, n, v): self.name = n self.value = v return named_value(name, value) # или просто class nv: pass nv.name = name nv.value = value return nv $$ LANGUAGE plpythonu;
Также поддерживаются функции с параметрами OUT
(выходными). Например:
CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ return (1, 2) $$ LANGUAGE plpythonu; SELECT * FROM multiout_simple();
Выходные параметры процедуры выдаются таким же образом. Например:
CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$ return (a * 3, b * 3) $$ LANGUAGE plpythonu; CALL python_triple(5, 10);
48.3.5. Функции, возвращающие множества
Функция PL/Python также может возвращать множества, содержащие скалярные и составные типы. Это можно осуществить разными способами, так как возвращаемый объект внутри превращается в итератор. В следующих примерах предполагается, что у нас есть составной тип:
CREATE TYPE greeting AS ( how text, who text );
Множество в качестве результата можно возвратить, применив:
- Последовательность (кортеж, список, множество)
CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ # возвращает кортеж, содержащий списки в качестве составных типов # также будут работать и остальные комбинации return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] ) $$ LANGUAGE plpythonu;
- Итератор (любой объект, реализующий методы
__iter__
иnext
) CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ class producer: def __init__ (self, how, who): self.how = how self.who = who self.ndx = -1 def __iter__ (self): return self def next (self): self.ndx += 1 if self.ndx == len(self.who): raise StopIteration return ( self.how, self.who[self.ndx] ) return producer(how, [ "World", "PostgreSQL", "PL/Python" ]) $$ LANGUAGE plpythonu;
- Генератор (
yield
) CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ for who in [ "World", "PostgreSQL", "PL/Python" ]: yield ( how, who ) $$ LANGUAGE plpythonu;
Также поддерживаются функции, возвращающие множества, с параметрами OUT
(объявленные с RETURNS SETOF record
). Например:
CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$ return [(1, 2)] * n $$ LANGUAGE plpythonu; SELECT * FROM multiout_simple_setof(3);