44.2. Значения данных#

44.2. Значения данных

44.2. Значения данных

В целом, целью PL/Python является обеспечение "естественного" соответствия между мирами PostgreSQL и Python. Это определяет правила отображения данных, описанные ниже.

44.2.1. Сопоставление типов данных

Когда вызывается функция PL/Python, ее аргументы преобразуются из их типа данных PostgreSQL в соответствующий тип Python:

  • PostgreSQL boolean преобразуется в Python bool.

  • PostgreSQL smallint, int, bigint и oid преобразуются в Python int.

  • PostgreSQL real и double преобразуются в Python float.

  • PostgreSQL numeric преобразуется в Python Decimal. Этот тип импортируется из пакета cdecimal, если он доступен. В противном случае будет использоваться decimal.Decimal из стандартной библиотеки. cdecimal значительно быстрее чем decimal. Однако, начиная с Python 3.3, cdecimal был интегрирован в стандартную библиотеку под именем decimal, поэтому больше нет разницы.

  • PostgreSQL bytea преобразуется в Python bytes.

  • Все остальные типы данных, включая типы символьных строк PostgreSQL, преобразуются в тип str Python (в формате Unicode, как и все строки в Python).

  • Для нескалярных типов данных см. ниже.

Когда функция PL/Python возвращает значение, оно преобразуется в объявленный тип данных возвращаемого значения PostgreSQL следующим образом:

  • Когда тип возвращаемого значения PostgreSQL является boolean, возвращаемое значение будет оцениваться на истинность в соответствии с правилами Python. То есть, 0 и пустая строка являются ложными, но заметим, что 'f' является true.

  • Когда тип возвращаемого значения PostgreSQL является bytea, возвращаемое значение будет преобразовано в тип bytes Python с использованием соответствующих встроенных функций Python, и результат будет преобразован в bytea.

  • Для всех остальных типов возвращаемых значений PostgreSQL, возвращаемое значение преобразуется в строку с использованием встроенной функции Python str, и результат передается во входную функцию типа данных PostgreSQL. (Если значение Python является типом float, оно преобразуется с использованием встроенной функции repr вместо str, чтобы избежать потери точности).

    Строки автоматически преобразуются в кодировку сервера PostgreSQL при передаче их в PostgreSQL.

  • Для нескалярных типов данных см. ниже.

Обратите внимание, что логические несоответствия между объявленным типом возвращаемого значения PostgreSQL и типом данных Python фактического возвращаемого объекта не будут отмечены; значение будет преобразовано в любом случае.

44.2.2. Null, None

Если значение SQL равно null Если аргумент None передается в функцию, то значение аргумента будет отображаться как None в Python. Например, определение функции pymax, показанное в Раздел 44.1, вернет неправильный ответ для пустых входных данных. Мы можем добавить STRICT к определению функции, чтобы Tantor SE выполнил что-то более корректно: если передается пустое значение, функция вообще не будет вызываться, а просто автоматически вернет пустой результат. В качестве альтернативы, мы можем проверить пустые входные данные в теле функции:

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 plpython3u;

Как показано выше, чтобы вернуть SQL-значение NULL из функции PL/Python, верните значение None. Это можно сделать независимо от того, является ли функция строгой или нет.

44.2.3. Массивы, cписки

SQL значения массива передаются в PL/Python в виде списка Python. Чтобы вернуть SQL значение массива из функции PL/Python, верните список Python:

CREATE FUNCTION return_arr()
  RETURNS int[]
AS $$
return [1, 2, 3, 4, 5]
$$ LANGUAGE plpython3u;

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 plpython3u;

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 plpython3u;

SELECT return_str_arr();
 return_str_arr
----------------
 {h,e,l,l,o}
(1 row)

44.2.4. Составные типы

Аргументы типа Composite передаются в функцию как отображения 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 plpython3u;

Есть несколько способов вернуть строки или составные типы из функции Python. В следующих примерах предполагается, что у нас есть:

CREATE TYPE named_value AS (
  name   text,
  value  integer
);

Композитный результат может быть возвращен в виде:

Sequence type (a tuple or list, but not a set because it is not indexable)

Возвращаемые объекты последовательности должны иметь такое же количество элементов, как и полей в составном типе результата. Элемент с индексом 0 присваивается первому полю составного типа, 1 - второму и так далее. Например:

CREATE FUNCTION make_pair (name text, value integer)
  RETURNS named_value
AS $$
  return ( name, value )
  # or alternatively, as list: return [ name, value ]
$$ LANGUAGE plpython3u;

Чтобы вернуть SQL null для любого столбца, вставьте None на соответствующую позицию.

Когда возвращается массив составных типов, его нельзя вернуть в виде списка, потому что неясно, представляет ли список Python составной тип или другое измерение массива.

Mapping (dictionary)

Значение для каждого столбца типа результата извлекается из отображения с именем столбца в качестве ключа. Пример:

CREATE FUNCTION make_pair (name text, value integer)
  RETURNS named_value
AS $$
  return { "name": name, "value": value }
$$ LANGUAGE plpython3u;

Любые дополнительные пары ключ/значение в словаре игнорируются. Отсутствующие ключи считаются ошибками. Для возврата значения SQL NULL для любого столбца вставьте None с соответствующим именем столбца в качестве ключа.

Object (any object providing method __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)

  # or simply
  class nv: pass
  nv.name = name
  nv.value = value
  return nv
$$ LANGUAGE plpython3u;

Функции с параметрами OUT также поддерживаются. Например:

CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
return (1, 2)
$$ LANGUAGE plpython3u;

SELECT * FROM multiout_simple();

Выходные параметры процедур передаются обратно таким же способом. Например:

CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$
return (a * 3, b * 3)
$$ LANGUAGE plpython3u;

CALL python_triple(5, 10);

44.2.5. Функции, возвращающие наборы значений

A PL/Python функция также может возвращать наборы скалярных или составных типов. Существует несколько способов достижения этого, поскольку возвращаемый объект внутренне преобразуется в итератор. Следующие примеры предполагают, что у нас есть составной тип:

CREATE TYPE greeting AS (
  how text,
  who text
);

Результатом запроса может быть набор значений:

Sequence type (tuple, list, set)

CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  # return tuple containing lists as composite types
  # all other combinations work also
  return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] )
$$ LANGUAGE plpython3u;

Iterator (any object providing __iter__ and next methods)

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 plpython3u;

Generator (yield)

CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  for who in [ "World", "PostgreSQL", "PL/Python" ]:
    yield ( how, who )
$$ LANGUAGE plpython3u;

Функции, возвращающие наборы значений с параметрами OUT (используя RETURNS SETOF record), также поддерживаются. Например:

CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$
return [(1, 2)] * n
$$ LANGUAGE plpython3u;

SELECT * FROM multiout_simple_setof(3);