45.6. ÐбÑаÑение к базе даннÑÑ
ÐÑполниÑÐµÐ»Ñ ÑзÑка PL/Python авÑомаÑиÑеÑки импоÑÑиÑÑÐµÑ Ð¼Ð¾Ð´ÑÐ»Ñ Python Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ plpy. ÐÑ Ð² ÑвоÑм коде можеÑе иÑполÑзоваÑÑ ÑÑнкÑии и конÑÑанÑÑ, обÑÑвленнÑе в ÑÑом модÑле, обÑаÑаÑÑÑ Ðº ним по именам вида plpy..имÑ
45.6.1. ФÑнкÑии обÑаÑÐµÐ½Ð¸Ñ Ðº базе даннÑÑ
ÐодÑÐ»Ñ plpy ÑодеÑÐ¶Ð¸Ñ ÑазлиÑнÑе ÑÑнкÑии Ð´Ð»Ñ Ð²ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´ в базе даннÑÑ
:
plpy.execute(query[,пÑедел])ÐÑи вÑзове
plpy.executeÑо ÑÑÑокой запÑоÑа и необÑзаÑелÑнÑм аÑгÑменÑом, огÑаниÑиваÑÑим ÑиÑло ÑÑÑок, вÑполнÑеÑÑÑ Ð·Ð°Ð´Ð°Ð½Ð½Ñй запÑоÑ, а Ñо, ÑÑо он вÑдаÑÑ, возвÑаÑаеÑÑÑ Ð² виде обÑекÑа ÑезÑлÑÑаÑа.ÐÑли
пÑеделзадан и болÑÑе нÑлÑ, Ñоplpy.executeполÑÑÐ°ÐµÑ ÑиÑло ÑÑÑок, не пÑевÑÑаÑÑеепÑедел, как еÑли Ð±Ñ Ð·Ð°Ð¿ÑÐ¾Ñ Ð²ÐºÐ»ÑÑал пÑедложениеLIMIT. Ðез ÑказаниÑпÑеделаили когда он Ñавен нÑÐ»Ñ Ð¾Ð³ÑаниÑение на колиÑеÑÑво ÑÑÑок ÑнимаеÑÑÑ.ÐбÑÐµÐºÑ ÑезÑлÑÑаÑа имиÑиÑÑÐµÑ ÑпиÑок или ÑловаÑÑ. ÐолÑÑиÑÑ Ð¸Ð· него даннÑе можно по номеÑÑ ÑÑÑоки и имени ÑÑолбÑа. ÐапÑимеÑ, команда:
rv = plpy.execute("SELECT * FROM my_table", 5)веÑнÑÑ Ð½Ðµ более 5 ÑÑÑок из оÑноÑениÑ
my_table. ÐÑли вmy_tableеÑÑÑ ÑÑолбеÑmy_column, к Ð½ÐµÐ¼Ñ Ð¼Ð¾Ð¶Ð½Ð¾ обÑаÑиÑÑÑÑ Ñак:foo = rv[i]["my_column"]
ЧиÑло возвÑаÑÑннÑÑ Ð² ÑÑом обÑекÑе ÑÑÑок можно полÑÑиÑÑ, воÑполÑзовавÑиÑÑ Ð²ÑÑÑоенной ÑÑнкÑией
len.ÐÐ»Ñ Ð¾Ð±ÑекÑа ÑезÑлÑÑаÑа опÑÐµÐ´ÐµÐ»ÐµÐ½Ñ ÑледÑÑÑие дополниÑелÑнÑе меÑодÑ:
nrows()ÐозвÑаÑÐ°ÐµÑ ÑиÑло ÑÑÑок, обÑабоÑаннÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾Ð¹. ÐамеÑÑÑе, ÑÑо ÑÑо ÑиÑло не обÑзаÑелÑно бÑÐ´ÐµÑ Ñавно ÑиÑÐ»Ñ Ð²Ð¾Ð·Ð²ÑаÑÑннÑÑ ÑÑÑок. ÐапÑимеÑ, команда
UPDATEÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ ÑÑо знаÑение, но не возвÑаÑÐ°ÐµÑ ÑÑÑок (без ÑказаниÑRETURNING).status()ÐнаÑение ÑоÑÑоÑниÑ, возвÑаÑÑнное
SPI_execute().colnames()coltypes()coltypmods()ÐозвÑаÑаÑÑ ÑпиÑок имÑн ÑÑолбÑов, ÑпиÑок OID Ñипов ÑÑолбÑов и ÑпиÑок модиÑикаÑоÑов Ñипа ÑÑÐ¸Ñ ÑÑолбÑов, ÑооÑвеÑÑÑвенно.
ÐÑи меÑÐ¾Ð´Ñ Ð²ÑзÑваÑÑ Ð¸ÑклÑÑение, когда им пеÑедаÑÑÑÑ Ð¾Ð±ÑекÑ, полÑÑеннÑй Ð¾Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ, не возвÑаÑаÑÑей ÑезÑлÑÑиÑÑÑÑий набоÑ, напÑимеÑ,
UPDATEбезRETURNING, либоDROP TABLE. Ðо ÑÑи меÑÐ¾Ð´Ñ Ð²Ð¿Ð¾Ð»Ð½Ðµ можно иÑполÑзоваÑÑ Ñ ÑезÑлÑÑаÑом, ÑодеÑжаÑим Ð½Ð¾Ð»Ñ ÑÑÑок.__str__()СÑандаÑÑнÑй меÑод
__str__опÑеделÑн Ñак, ÑÑÐ¾Ð±Ñ Ð¼Ð¾Ð¶Ð½Ð¾ бÑло, напÑимеÑ, вÑвеÑÑи оÑладоÑное ÑообÑение Ñ ÑезÑлÑÑаÑами запÑоÑа, вÑзвавplpy.debug(rv).
ÐбÑÐµÐºÑ ÑезÑлÑÑаÑа Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¸Ð·Ð¼ÐµÐ½Ñн.
ÐамеÑÑÑе, ÑÑо пÑи вÑзове
plpy.executeвеÑÑ Ð½Ð°Ð±Ð¾Ñ ÑезÑлÑÑаÑов бÑÐ´ÐµÑ Ð¿ÑоÑиÑан в памÑÑÑ. ÐÑÑ ÑÑнкÑÐ¸Ñ ÑледÑÐµÑ Ð¸ÑполÑзоваÑÑ, ÑолÑко еÑли Ð²Ñ Ð·Ð½Ð°ÐµÑе, ÑÑо Ð½Ð°Ð±Ð¾Ñ Ð±ÑÐ´ÐµÑ Ð¾ÑноÑиÑелÑно неболÑÑим. ÐÑли Ð²Ñ Ñ Ð¾ÑиÑе иÑклÑÑиÑÑ ÑиÑк пеÑÐµÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿Ð°Ð¼ÑÑи пÑи вÑбоÑке ÑезÑлÑÑаÑов болÑÑого обÑÑма, иÑполÑзÑйÑеplpy.cursorвмеÑÑоplpy.execute.plpy.prepare(query[,ÑипÑ_аÑгÑменÑов])plpy.execute(план[,аÑгÑменÑÑ[,пÑедел]])ФÑнкÑиÑ
plpy.prepareподгоÑÐ°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð¿Ð»Ð°Ð½ вÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð·Ð°Ð¿ÑоÑа. Ðна вÑзÑваеÑÑÑ Ñо ÑÑÑокой запÑоÑа и ÑпиÑком Ñипов паÑамеÑÑов (еÑли в запÑоÑе еÑÑÑ Ð¿Ð°ÑамеÑÑÑ). ÐапÑимеÑ:plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])ÐдеÑÑ
textпÑедÑÑавлÑÐµÑ Ð¿ÐµÑеменнÑÑ, пеÑедаваемÑÑ Ð² каÑеÑÑве паÑамеÑÑа$1. ÐÑоÑой аÑгÑÐ¼ÐµÐ½Ñ Ð½ÐµÐ¾Ð±ÑзаÑелен, еÑли запÑоÑÑ Ð½Ðµ нÑжно пеÑедаваÑÑ Ð½Ð¸ÐºÐ°ÐºÐ¸Ðµ паÑамеÑÑÑ.ЧÑÐ¾Ð±Ñ Ð·Ð°Ð¿ÑÑÑиÑÑ Ð¿Ð¾Ð´Ð³Ð¾ÑовленнÑй опеÑаÑÐ¾Ñ Ð½Ð° вÑполнение, иÑполÑзÑйÑе ваÑиаÑÐ¸Ñ ÑÑнкÑии
plpy.execute:rv = plpy.execute(plan, ["name"], 5)
ÐеÑедайÑе план в пеÑвом аÑгÑменÑе (вмеÑÑо ÑÑÑоки запÑоÑа), а ÑпиÑок знаÑений, коÑоÑÑе бÑдÑÑ Ð¿Ð¾Ð´ÑÑÐ°Ð²Ð»ÐµÐ½Ñ Ð² запÑоÑ, â во вÑоÑом. ÐÑоÑой аÑгÑÐ¼ÐµÐ½Ñ Ð¼Ð¾Ð¶Ð½Ð¾ опÑÑÑиÑÑ, еÑли запÑÐ¾Ñ Ð½Ðµ пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð½Ð¸ÐºÐ°ÐºÐ¸Ðµ паÑамеÑÑÑ. ТÑеÑий аÑгÑменÑ, как и ÑанÑÑе, задаÑÑ Ð½ÐµÐ¾Ð±ÑзаÑелÑное огÑаниÑение макÑималÑного ÑиÑла ÑÑÑок.
ÐÑ Ñакже можеÑе вÑзваÑÑ Ð¼ÐµÑод
executeобÑекÑа плана:rv = plan.execute(["name"], 5)
ÐаÑамеÑÑÑ Ð·Ð°Ð¿ÑоÑов и Ð¿Ð¾Ð»Ñ ÑÑÑок ÑезÑлÑÑаÑа пÑеобÑазÑÑÑÑÑ Ð¼ÐµÐ¶Ð´Ñ Ñипами даннÑÑ PostgreSQL и Python как опиÑано в Разделе 45.2.
Ðогда Ð²Ñ Ð¿Ð¾Ð´Ð³Ð¾ÑавливаеÑе план, иÑполÑзÑÑ Ð¼Ð¾Ð´ÑÐ»Ñ PL/Python, он ÑÐ¾Ñ ÑанÑеÑÑÑ Ð°Ð²ÑомаÑиÑеÑки. ЧÑо ÑÑо ознаÑаеÑ, Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе ÑзнаÑÑ Ð² докÑменÑаÑии SPI (Ðлава 46). ЧÑÐ¾Ð±Ñ ÑÑÑекÑивно иÑполÑзоваÑÑ ÑÑо в неÑколÑÐºÐ¸Ñ Ð²ÑÐ·Ð¾Ð²Ð°Ñ ÑÑнкÑии, Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾ÑÑебоваÑÑÑÑ Ð¿ÑимениÑÑ ÑловаÑÑ Ð¿Ð¾ÑÑоÑнного Ñ ÑанениÑ
SDилиGD(Ñм. Раздел 45.3). ÐапÑимеÑ:CREATE FUNCTION usesavedplan() RETURNS trigger AS $$ if "plan" in SD: plan = SD["plan"] else: plan = plpy.prepare("SELECT 1") SD["plan"] = plan # оÑÑалÑной код ÑÑнкÑии $$ LANGUAGE plpython3u;plpy.cursor(query)plpy.cursor(план[,аÑгÑменÑÑ])ФÑнкÑиÑ
plpy.cursorпÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ñе же аÑгÑменÑÑ, ÑÑо иplpy.execute(кÑоме огÑаниÑÐµÐ½Ð¸Ñ ÑÑÑок) и возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÐµÐºÑ ÐºÑÑÑоÑа, коÑоÑÑй позволÑÐµÑ Ð¾Ð±ÑабаÑÑваÑÑ Ð¾Ð±ÑÑмнÑе набоÑÑ ÑезÑлÑÑаÑов неболÑÑими поÑÑиÑми. Ðак иplpy.execute, ÑÑой ÑÑнкÑии можно пеÑедаÑÑ ÑÑÑÐ¾ÐºÑ Ð·Ð°Ð¿ÑоÑа или обÑÐµÐºÑ Ð¿Ð»Ð°Ð½Ð° Ñо ÑпиÑком аÑгÑменÑов, а можно вÑзÑваÑÑ ÑÑнкÑиÑcursorкак меÑод обÑекÑа плана.ÐбÑÐµÐºÑ ÐºÑÑÑоÑа ÑеализÑÐµÑ Ð¼ÐµÑод
fetch, коÑоÑÑй пÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑелоÑиÑленнÑй паÑамеÑÑ Ð¸ возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÐµÐºÑ ÑезÑлÑÑаÑа. ÐÑи каждом ÑледÑÑÑем вÑзовеfetchвозвÑаÑаемÑй обÑÐµÐºÑ Ð±ÑÐ´ÐµÑ ÑодеÑжаÑÑ ÑледÑÑÑий Ð½Ð°Ð±Ð¾Ñ ÑÑÑок, в колиÑеÑÑве, не пÑевÑÑаÑÑем знаÑение паÑамеÑÑа. Ðогда ÑÑÑоки законÑаÑÑÑ,fetchнаÑнÑÑ Ð²Ð¾Ð·Ð²ÑаÑаÑÑ Ð¿ÑÑÑой обÑÐµÐºÑ ÑезÑлÑÑаÑа. ÐбÑекÑÑ ÐºÑÑÑоÑа Ñакже пÑедоÑÑавлÑÑÑ Ð¸Ð½ÑеÑÑÐµÐ¹Ñ Ð¸ÑеÑаÑоÑа, вÑдаÑÑий по ÑÑÑоке за один Ñаз, пока не бÑдÑÑ Ð²ÑÐ´Ð°Ð½Ñ Ð²Ñе ÑÑÑоки. ÐаннÑе, вÑбиÑаемÑе Ñаким обÑазом, возвÑаÑаÑÑÑÑ Ð½Ðµ как обÑекÑÑ ÑезÑлÑÑаÑа, а как ÑловаÑи (одной ÑÑÑоке ÑезÑлÑÑаÑа ÑооÑвеÑÑÑвÑÐµÑ Ð¾Ð´Ð¸Ð½ ÑловаÑÑ).СледÑÑÑий пÑÐ¸Ð¼ÐµÑ Ð´ÐµÐ¼Ð¾Ð½ÑÑÑиÑÑÐµÑ Ð¾Ð±ÑабоÑÐºÑ ÑодеÑжимого болÑÑой ÑаблиÑÑ Ð´Ð²ÑÐ¼Ñ ÑпоÑобами:
CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$ odd = 0 for row in plpy.cursor("select num from largetable"): if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpython3u; CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$ odd = 0 cursor = plpy.cursor("select num from largetable") while True: rows = cursor.fetch(batch_size) if not rows: break for row in rows: if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpython3u; CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$ odd = 0 plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"]) rows = list(plpy.cursor(plan, [2])) # или: = list(plan.cursor([2])) return len(rows) $$ LANGUAGE plpython3u;ÐÑÑÑоÑÑ Ð»Ð¸ÐºÐ²Ð¸Ð´Ð¸ÑÑÑÑÑÑ Ð°Ð²ÑомаÑиÑеÑки. Ðо еÑли Ð²Ñ Ñ Ð¾ÑиÑе Ñвно оÑвободиÑÑ Ð²Ñе ÑеÑÑÑÑÑ, занÑÑÑе кÑÑÑоÑом, вÑзовиÑе меÑод
close. ÐÑодолжаÑÑ Ð¿Ð¾Ð»ÑÑаÑÑ Ð´Ð°Ð½Ð½Ñе ÑеÑез кÑÑÑоÑ, коÑоÑÑй бÑл закÑÑÑ, нелÑзÑ.ÐодÑказка
Ðе пÑÑайÑе обÑекÑÑ, ÑоздаваемÑе ÑÑнкÑией
plpy.cursor, Ñ ÐºÑÑÑоÑами DB-API, опÑеделÑннÑми в ÑпеÑиÑикаÑии API Ð´Ð»Ñ ÑабоÑÑ Ñ Ð±Ð°Ð·Ð°Ð¼Ð¸ даннÑÑ Ð² Python. Ðни не имеÑÑ Ð½Ð¸Ñего обÑего, кÑоме имени.
45.6.2. ÐбÑабоÑка оÑибок
ФÑнкÑии, обÑаÑаÑÑиеÑÑ Ðº базе даннÑÑ
, могÑÑ ÑÑалкиваÑÑÑÑ Ñ Ð¾Ñибками, в ÑезÑлÑÑаÑе коÑоÑÑÑ
они бÑдÑÑ Ð¿ÑеÑÑваÑÑÑÑ Ð¸ вÑзÑваÑÑ Ð¸ÑклÑÑение. Ðбе ÑÑнкÑии plpy.execute и plpy.prepare могÑÑ Ð²ÑзÑваÑÑ ÑкземплÑÑ Ð¿Ð¾Ð´ÐºÐ»Ð°ÑÑа иÑклÑÑÐµÐ½Ð¸Ñ plpy.SPIError, коÑоÑое по ÑмолÑание пÑекÑаÑÐ°ÐµÑ Ð²Ñполнение ÑÑнкÑии. ÐÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¼Ð¾Ð¶Ð½Ð¾ обÑабоÑаÑÑ, как и лÑбое дÑÑгое иÑклÑÑение в Python, пÑименив конÑÑÑÑкÑÐ¸Ñ try/except. ÐапÑимеÑ:
CREATE FUNCTION try_adding_joe() RETURNS text AS $$
try:
plpy.execute("INSERT INTO users(username) VALUES ('joe')")
except plpy.SPIError:
return "something went wrong"
else:
return "Joe added"
$$ LANGUAGE plpython3u;ФакÑиÑеÑкий клаÑÑ Ð²ÑзÑваемого иÑклÑÑÐµÐ½Ð¸Ñ ÑооÑвеÑÑÑвÑÐµÑ Ð¾Ð¿ÑеделÑÐ½Ð½Ð¾Ð¼Ñ ÑÑÐ»Ð¾Ð²Ð¸Ñ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ Ð¾Ñибки. СпиÑок вÑеÑ
возможнÑÑ
ÑÑловий пÑиведÑн в ТаблиÑе A.1. РмодÑле plpy.spiexceptions опÑеделÑÑÑÑÑ ÐºÐ»Ð°ÑÑÑ Ð¸ÑклÑÑений Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑÑÐ»Ð¾Ð²Ð¸Ñ Postgres Pro, Ñ Ð¸Ð¼ÐµÐ½Ð°Ð¼Ð¸, пÑоизводнÑми Ð¾Ñ Ð¸Ð¼Ñн ÑÑловий. ÐапÑимеÑ, Ð¸Ð¼Ñ division_by_zero ÑÑановиÑÑÑ Ð¸Ð¼ÐµÐ½ÐµÐ¼ DivisionByZero, unique_violation â именем UniqueViolation, fdw_error â именем FdwError и Ñ. д. ÐÑе ÑÑи клаÑÑÑ Ð¸ÑклÑÑений наÑледÑÑÑÑÑ Ð¾Ñ SPIError. Такое Ñазделение на клаÑÑÑ ÑпÑоÑÐ°ÐµÑ Ð¾Ð±ÑабоÑÐºÑ Ð¾Ð¿ÑеделÑннÑÑ
оÑибок, напÑимеÑ:
CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$
from plpy import spiexceptions
try:
plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
plpy.execute(plan, [numerator, denominator])
except spiexceptions.DivisionByZero:
return "denominator cannot equal zero"
except spiexceptions.UniqueViolation:
return "already have that fraction"
except plpy.SPIError as e:
return "other error, SQLSTATE %s" % e.sqlstate
else:
return "fraction inserted"
$$ LANGUAGE plpython3u; ÐамеÑÑÑе, ÑÑо Ñак как вÑе иÑклÑÑÐµÐ½Ð¸Ñ Ð¸Ð· модÑÐ»Ñ plpy.spiexceptions наÑледÑÑÑÑÑ Ð¾Ñ Ð¸ÑклÑÑÐµÐ½Ð¸Ñ SPIError, команда except, обÑабаÑÑваÑÑÐ°Ñ ÑÑо иÑклÑÑение, бÑÐ´ÐµÑ Ð¿ÐµÑеÑ
ваÑÑваÑÑ Ð²Ñе оÑибки пÑи обÑаÑении к базе даннÑÑ
.
РкаÑеÑÑве дÑÑгого ваÑианÑа обÑабоÑки ÑазлиÑнÑÑ
ÑÑловий оÑибок, Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе пеÑеÑ
ваÑÑваÑÑ Ð¸ÑклÑÑение SPIError и опÑеделÑÑÑ ÐºÐ¾Ð½ÐºÑеÑное ÑÑловие оÑибки внÑÑÑи блока except по знаÑÐµÐ½Ð¸Ñ Ð°ÑÑибÑÑа sqlstate обÑекÑа иÑклÑÑениÑ. ÐÑÐ¾Ñ Ð°ÑÑибÑÑ ÑодеÑÐ¶Ð¸Ñ ÑÑÑÐ¾ÐºÑ Ñ ÐºÐ¾Ð´Ð¾Ð¼ оÑибки «SQLSTATE». ÐонеÑнÑй ÑезÑлÑÑÐ°Ñ Ð¿Ñи Ñаком подÑ
оде пÑимеÑно ÑÐ¾Ñ Ð¶Ðµ.