45.2. ÐнаÑÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½ÑÑ
ÐообÑе говоÑÑ, ÑÐµÐ»Ñ Ð¸ÑполниÑÐµÐ»Ñ PL/Python â обеÑпеÑиÑÑ Â«ÐµÑÑеÑÑвенное» ÑооÑвеÑÑÑвие Ð¼ÐµÐ¶Ð´Ñ Ð¼Ð¸Ñами PostgreSQL и Python. ÐÑим обÑÑÑнÑеÑÑÑ Ð²ÑÐ±Ð¾Ñ Ð¿Ñавил ÑопоÑÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½ÑÑ , опиÑаннÑÑ Ð½Ð¸Ð¶Ðµ.
45.2.1. СопоÑÑавление Ñипов даннÑÑ
Ðогда вÑзÑваеÑÑÑ ÑÑнкÑÐ¸Ñ PL/Python, ÐµÑ Ð°ÑгÑменÑÑ Ð¿ÑеобÑазÑÑÑÑÑ Ð¸Ð· Ñипа PostgreSQL в ÑооÑвеÑÑÑвÑÑÑий Ñип Python по Ñаким пÑавилам:
Тип PostgreSQL
booleanпÑеобÑазÑеÑÑÑ Ð² ÑипboolÑзÑка Python.Ð¢Ð¸Ð¿Ñ PostgreSQL
smallint,int,bigintиoidпÑеобÑазÑÑÑÑÑ Ð² ÑипintÑзÑка Python.Ð¢Ð¸Ð¿Ñ PostgreSQL
realиdoubleпÑеобÑазÑÑÑÑÑ Ð² ÑипfloatÑзÑка Python.Тип PostgreSQL
numericпÑеобÑазÑеÑÑÑ Ð² ÑипDecimalÑÑÐµÐ´Ñ Python. ÐÑÐ¾Ñ Ñип импоÑÑиÑÑеÑÑÑ Ð¸Ð· пакеÑаcdecimal, пÑи его налиÑии. РпÑоÑивном ÑлÑÑае иÑполÑзÑеÑÑÑdecimal.Decimalиз ÑÑандаÑÑной библиоÑеки. ТипcdecimalÑабоÑÐ°ÐµÑ Ð·Ð½Ð°ÑиÑелÑно бÑÑÑÑее, Ñемdecimal. Ðднако в Python веÑÑии 3.3 и вÑÑе ÑипcdecimalвклÑÑаеÑÑÑ Ð² ÑÑандаÑÑнÑÑ Ð±Ð¸Ð±Ð»Ð¸Ð¾ÑÐµÐºÑ Ð¿Ð¾Ð´ именемdecimal, Ñак ÑÑо ÑепеÑÑ ÑÑого ÑазлиÑÐ¸Ñ Ð½ÐµÑ.Тип PostgreSQL
byteaпÑеобÑазÑеÑÑÑ Ð² ÑипbytesÑзÑка Python.ÐÑе дÑÑгие ÑÐ¸Ð¿Ñ Ð´Ð°Ð½Ð½ÑÑ , вклÑÑÐ°Ñ ÑÐ¸Ð¿Ñ ÑимволÑнÑÑ ÑÑÑок PostgreSQL, пÑеобÑазÑÑÑÑÑ Ð² Ñип
strÑзÑка Python (ÑÑÑока в Unicode, как и вÑе ÑÑÑоки Python).ÐнÑоÑмаÑÐ¸Ñ Ð¾ неÑкалÑÑнÑÑ ÑÐ¸Ð¿Ð°Ñ Ð´Ð°Ð½Ð½ÑÑ Ð¿Ñиведена ниже.
ÐÑи завеÑÑении ÑÑнкÑии PL/Python ÐµÑ Ð·Ð½Ð°Ñение ÑезÑлÑÑаÑа пÑеобÑазÑеÑÑÑ Ð² Ñип даннÑÑ , обÑÑвленнÑй как Ñип ÑезÑлÑÑаÑа в PostgreSQL, ÑледÑÑÑим обÑазом:
Ðогда Ñип ÑезÑлÑÑаÑа ÑÑнкÑии в PostgreSQL â
boolean, возвÑаÑаемое знаÑение пÑиводиÑÑÑ Ðº логиÑеÑÐºÐ¾Ð¼Ñ ÑÐ¸Ð¿Ñ Ð¿Ð¾ пÑавилам, пÑинÑÑÑм в Python. То еÑÑÑ false бÑÐ´ÐµÑ Ð²Ð¾Ð·Ð²ÑаÑено Ð´Ð»Ñ 0 и пÑÑÑой ÑÑÑоки, но, обÑаÑиÑе внимание, длÑ'f'бÑÐ´ÐµÑ Ð²Ð¾Ð·Ð²ÑаÑено true.Ðогда Ñип ÑезÑлÑÑаÑа ÑÑнкÑии PostgreSQL â
bytea, возвÑаÑаемое знаÑение бÑÐ´ÐµÑ Ð¿ÑеобÑазовано в Ð½Ð°Ð±Ð¾Ñ Ð±Ð°Ð¹Ñ, иÑполÑзÑÑ Ð²ÑÑÑоеннÑе ÑÑедÑÑва Python, а заÑем бÑÐ´ÐµÑ Ð¿Ñиведено к ÑипÑbytea.ÐÐ»Ñ Ð²ÑÐµÑ Ð´ÑÑÐ³Ð¸Ñ Ñипов ÑезÑлÑÑаÑа PostgreSQL возвÑаÑаемое знаÑение пÑеобÑазÑеÑÑÑ Ð² ÑÑÑÐ¾ÐºÑ Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Ð²ÑÑÑоенной в Python ÑÑнкÑии
str, и полÑÑÐµÐ½Ð½Ð°Ñ ÑÑÑока пеÑедаÑÑÑÑ ÑÑнкÑии ввода Ñипа даннÑÑ PostgreSQL. (ÐÑли знаÑение в Python Ð¸Ð¼ÐµÐµÑ Ñипfloat, оно пÑеобÑазÑеÑÑÑ Ð²ÑÑÑоенной ÑÑнкÑиейrepr, а неstr, Ð´Ð»Ñ Ð½ÐµÐ´Ð¾Ð¿ÑÑÐµÐ½Ð¸Ñ Ð¿Ð¾ÑеÑи ÑоÑноÑÑи.)ÐÑи пеÑедаÑе в PostgreSQL ÑÑÑоки авÑомаÑиÑеÑки пÑеобÑазÑÑÑÑÑ Ð² кодиÑÐ¾Ð²ÐºÑ ÑеÑвеÑа PostgreSQL.
ÐнÑоÑмаÑÐ¸Ñ Ð¾ неÑкалÑÑнÑÑ ÑÐ¸Ð¿Ð°Ñ Ð´Ð°Ð½Ð½ÑÑ Ð¿Ñиведена ниже.
ÐамеÑÑÑе, ÑÑо логиÑеÑкие неÑооÑвеÑÑÑÐ²Ð¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð¾Ð±ÑÑвленнÑм в PostgreSQL Ñипом ÑезÑлÑÑаÑа и Ñипом ÑакÑиÑеÑки возвÑаÑаемого обÑекÑа Python игноÑиÑÑÑÑÑÑ â знаÑение пÑеобÑазÑеÑÑÑ Ð² лÑбом ÑлÑÑае.
45.2.2. Null, None
ÐÑли ÑÑнкÑии пеÑедаÑÑÑÑ Ð·Ð½Ð°Ñение SQL NULL, в Python знаÑением ÑÑого аÑгÑменÑа бÑÐ´ÐµÑ None. ÐапÑимеÑ, ÑÑнкÑÐ¸Ñ pymax, опÑеделÑÐ½Ð½Ð°Ñ ÐºÐ°Ðº показано в Раздел 45.1, возвÑаÑÐ¸Ñ Ð½ÐµÐ²ÐµÑнÑй оÑвеÑ, полÑÑив аÑгÑменÑÑ 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 plpython3u; Ðак показано вÑÑе, ÑÑÐ¾Ð±Ñ Ð²ÑдаÑÑ Ð¸Ð· ÑÑнкÑии PL/Python знаÑение SQL NULL, нÑжно веÑнÑÑÑ Ð·Ð½Ð°Ñение None. ÐÑо можно ÑделаÑÑ Ð¸ в ÑÑÑогой, и в неÑÑÑогой ÑÑнкÑии.
45.2.3. ÐаÑÑивÑ, ÑпиÑки
ÐнаÑÐµÐ½Ð¸Ñ Ð¼Ð°ÑÑивов 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)45.2.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 plpython3u;ÐозвÑаÑиÑÑ ÑоÑÑавной Ñип или ÑÑÑÐ¾ÐºÑ ÑаблиÑÑ Ð¸Ð· ÑÑнкÑии 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 plpython3u;
ЧÑÐ¾Ð±Ñ Ð²ÑдаÑÑ SQL NULL Ð´Ð»Ñ ÐºÐ°ÐºÐ¾Ð³Ð¾-нибÑÐ´Ñ ÑÑолбÑа, вÑÑавÑÑе в ÑооÑвеÑÑÑвÑÑÑÑÑ Ð¿Ð¾Ð·Ð¸ÑиÑ
None.Ðогда возвÑаÑаеÑÑÑ Ð¼Ð°ÑÑив ÑоÑÑавнÑÑ Ð·Ð½Ð°Ñений, его нелÑÐ·Ñ Ð¿ÑедÑÑавиÑÑ Ð² виде ÑпиÑка, Ñак как невозможно однознаÑно опÑеделиÑÑ, пÑедÑÑавлÑÐµÑ Ð»Ð¸ ÑпиÑок Python ÑоÑÑавной Ñип или еÑÑ Ð¾Ð´Ð½Ñ ÑазмеÑноÑÑÑ Ð¼Ð°ÑÑива.
- СопоÑÑавление (ÑловаÑÑ)
ÐнаÑение ÑÑолбÑа ÑезÑлÑÑаÑа полÑÑаеÑÑÑ Ð¸Ð· ÑопоÑÑавлениÑ, в коÑоÑом клÑÑом ÑвлÑеÑÑÑ Ð¸Ð¼Ñ ÑÑолбÑа. ÐапÑимеÑ:
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return { "name": name, "value": value } $$ LANGUAGE plpython3u;ÐÑбÑе дополниÑелÑнÑе паÑÑ ÐºÐ»ÑÑ/знаÑение в ÑловаÑе игноÑиÑÑÑÑÑÑ, а оÑÑÑÑÑÑвие нÑжнÑÑ ÐºÐ»ÑÑей ÑÑиÑаеÑÑÑ Ð¾Ñибкой. ЧÑÐ¾Ð±Ñ Ð²ÑдаÑÑ 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) # 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);
45.2.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 plpython3u;
- ÐÑеÑаÑÐ¾Ñ (лÑбой обÑекÑ, ÑеализÑÑÑий меÑодÑ
__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 plpython3u;- ÐенеÑаÑÐ¾Ñ (
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);