44.8. ЯвнÑе подÑÑанзакÑии
ÐеÑÐµÑ Ð²Ð°Ñ Ð¾Ñибок, пÑоизоÑедÑÐ¸Ñ Ð¿Ñи обÑаÑении к базе даннÑÑ , как опиÑано в ÐодÑазделе 44.7.2, Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑивеÑÑи к нежелаÑелÑной ÑиÑÑаÑии, когда ÑаÑÑÑ Ð¾Ð¿ÐµÑаÑий бÑÐ´ÐµÑ ÑÑпеÑно вÑполнена, пÑежде Ñем пÑоизойдÑÑ Ñбой. ÐаннÑе оÑÑанÑÑÑÑ Ð² неÑоглаÑованном ÑоÑÑоÑнии поÑле обÑабоÑки Ñакой оÑибки. PL/Python пÑÐµÐ´Ð»Ð°Ð³Ð°ÐµÑ ÑеÑение ÑÑой пÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ð² ÑоÑме ÑвнÑÑ Ð¿Ð¾Ð´ÑÑанзакÑий.
44.8.1. ÐенеджеÑÑ ÐºÐ¾Ð½ÑекÑÑа подÑÑанзакÑий
РаÑÑмоÑÑим ÑÑнкÑиÑ, оÑÑÑеÑÑвлÑÑÑÑÑ Ð¿ÐµÑевод ÑÑедÑÑв Ð¼ÐµÐ¶Ð´Ñ Ð´Ð²ÑÐ¼Ñ ÑÑеÑами:
CREATE FUNCTION transfer_funds() RETURNS void AS $$
try:
plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
except plpy.SPIError, e:
result = "error transferring funds: %s" % e.args
else:
result = "funds transferred correctly"
plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
plpy.execute(plan, [result])
$$ LANGUAGE plpythonu; ÐÑли пÑи вÑполнении вÑоÑого опеÑаÑоÑа UPDATE пÑоизойдÑÑ Ð¸ÑклÑÑение, ÑÑа ÑÑнкÑÐ¸Ñ ÑообÑÐ¸Ñ Ð¾Ð± оÑибке, но ÑезÑлÑÑÐ°Ñ Ð¿ÐµÑвого UPDATE бÑÐ´ÐµÑ Ñем не менее заÑикÑиÑован. ÐÑÑгими Ñловами, ÑÑедÑÑва бÑдÑÑ ÑпиÑÐ°Ð½Ñ Ñо ÑÑÑÑа Ðжо, но не заÑиÑлÑÑÑÑ Ð½Ð° ÑÑÑÑ ÐÑÑи.
Ðо избежание ÑакиÑ
пÑоблем Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе завеÑнÑÑÑ Ð²ÑÐ·Ð¾Ð²Ñ plpy.execute в ÑвнÑÑ Ð¿Ð¾Ð´ÑÑанзакÑиÑ. ÐодÑÐ»Ñ plpy пÑедоÑÑавлÑÐµÑ Ð²ÑпомогаÑелÑнÑй обÑÐµÐºÑ Ð´Ð»Ñ ÑпÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ ÑвнÑми подÑÑанзакÑиÑми, ÑоздаваемÑй ÑÑнкÑией plpy.subtransaction(). ÐбÑекÑÑ, ÑозданнÑе ÑÑой ÑÑнкÑией, ÑеализÑÑÑ Ð¸Ð½ÑеÑÑÐµÐ¹Ñ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑа конÑекÑÑа. ÐÑполÑзÑÑ ÑвнÑе подÑÑанзакÑии, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ пеÑепиÑаÑÑ Ð½Ð°ÑÑ ÑÑнкÑÐ¸Ñ Ñак:
CREATE FUNCTION transfer_funds2() RETURNS void AS $$
try:
with plpy.subtransaction():
plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
except plpy.SPIError, e:
result = "error transferring funds: %s" % e.args
else:
result = "funds transferred correctly"
plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
plpy.execute(plan, [result])
$$ LANGUAGE plpythonu; ÐамеÑÑÑе, ÑÑо конÑÑÑÑкÑÐ¸Ñ try/catch по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð½Ñжна. Ðез Ð½ÐµÑ Ð¸ÑклÑÑение ÑаÑпÑоÑÑÑаниÑÑÑ Ð²Ð²ÐµÑÑ
по ÑÑÐµÐºÑ Python и пÑиведÑÑ Ðº пÑеÑÑÐ²Ð°Ð½Ð¸Ñ Ð²Ñей ÑÑнкÑии Ñ Ð¾Ñибкой Postgres Pro, Ñак ÑÑо в ÑаблиÑÑ operations запиÑÑ Ð½Ðµ добавиÑÑÑ. ÐÐµÐ½ÐµÐ´Ð¶ÐµÑ ÐºÐ¾Ð½ÑекÑÑа подÑÑанзакÑии не пеÑеÑ
ваÑÑÐ²Ð°ÐµÑ Ð¾Ñибки, он ÑолÑко гаÑанÑиÑÑеÑ, ÑÑо вÑе опеÑаÑии Ñ Ð±Ð°Ð·Ð¾Ð¹ даннÑÑ
в его облаÑÑи дейÑÑÐ²Ð¸Ñ Ð±ÑдÑÑ Ð°ÑомаÑно заÑикÑиÑÐ¾Ð²Ð°Ð½Ñ Ð¸Ð»Ð¸ оÑмененÑ. ÐÑÐºÐ°Ñ Ð±Ð»Ð¾ÐºÐ° подÑÑанзакÑии пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð¿Ñи иÑклÑÑении лÑбого вида, а не ÑолÑко иÑклÑÑениÑ, вÑзванного оÑибками пÑи обÑаÑении к базе даннÑÑ
. ÐбÑÑное иÑклÑÑение Python, вÑзванное внÑÑÑи блока Ñвной подÑÑанзакÑии, Ñакже пÑиведÑÑ Ðº оÑкаÑÑ ÑÑой подÑÑанзакÑии.
44.8.2. СÑаÑÑе веÑÑии Python
СинÑакÑÐ¸Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑов конÑекÑÑа Ñ ÐºÐ»ÑÑевÑм Ñловом with по ÑмолÑÐ°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑживаеÑÑÑ Ð² Python 2.6. Ð PL/Python Ñ Ð±Ð¾Ð»ÐµÐµ ÑÑаÑой веÑÑией Python Ñоже возможно иÑполÑзоваÑÑ ÑвнÑе подÑÑанзакÑии, Ñ
оÑÑ Ð¸ не Ñак пÑозÑаÑно. ÐÑи ÑÑом Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе вÑзÑваÑÑ Ð¼ÐµÑÐ¾Ð´Ñ __enter__ и __exit__ менеджеÑа конÑекÑÑа по ÑдобнÑм пÑевдонимам enter и exit. ÐÐ»Ñ Ñакого ÑлÑÑÐ°Ñ ÑÑнкÑÐ¸Ñ Ð¿ÐµÑеÑиÑÐ»ÐµÐ½Ð¸Ñ ÑÑедÑÑв можно пеÑепиÑаÑÑ Ñак:
CREATE FUNCTION transfer_funds_old() RETURNS void AS $$
try:
subxact = plpy.subtransaction()
subxact.enter()
try:
plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
except:
import sys
subxact.exit(*sys.exc_info())
raise
else:
subxact.exit(None, None, None)
except plpy.SPIError, e:
result = "error transferring funds: %s" % e.args
else:
result = "funds transferred correctly"
plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
plpy.execute(plan, [result])
$$ LANGUAGE plpythonu;ÐÑимеÑание
ХоÑÑ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑÑ ÐºÐ¾Ð½ÑекÑÑа бÑли ÑÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ Ð² 2.5, Ð´Ð»Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑинÑакÑиÑа with в ÑÑой веÑÑии нÑжно пÑимениÑÑ Â«Ð±ÑдÑÑий опеÑаÑоÑ». Ðднако по ÑеÑ
ниÑеÑким пÑиÑинам «бÑдÑÑие опеÑаÑоÑÑ» в ÑÑнкÑиÑÑ
PL/Python иÑполÑзоваÑÑ Ð½ÐµÐ»ÑзÑ.