44.7. ЯвнÑе подÑÑанзакÑии #
ÐеÑÐµÑ Ð²Ð°Ñ Ð¾Ñибок, пÑоизоÑедÑÐ¸Ñ Ð¿Ñи обÑаÑении к базе даннÑÑ , как опиÑано в ÐодÑазделе 44.6.2, Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑивеÑÑи к нежелаÑелÑной ÑиÑÑаÑии, когда ÑаÑÑÑ Ð¾Ð¿ÐµÑаÑий бÑÐ´ÐµÑ ÑÑпеÑно вÑполнена, пÑежде Ñем пÑоизойдÑÑ Ñбой. ÐаннÑе оÑÑанÑÑÑÑ Ð² неÑоглаÑованном ÑоÑÑоÑнии поÑле обÑабоÑки Ñакой оÑибки. PL/Python пÑÐµÐ´Ð»Ð°Ð³Ð°ÐµÑ ÑеÑение ÑÑой пÑÐ¾Ð±Ð»ÐµÐ¼Ñ Ð² ÑоÑме ÑвнÑÑ Ð¿Ð¾Ð´ÑÑанзакÑий.
44.7.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 as 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 plpython3u; ÐÑли пÑи вÑполнении вÑоÑого опеÑаÑоÑа 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 as 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 plpython3u; ÐамеÑÑÑе, ÑÑо конÑÑÑÑкÑÐ¸Ñ try/except по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ Ð½Ñжна. Ðез Ð½ÐµÑ Ð¸ÑклÑÑение ÑаÑпÑоÑÑÑаниÑÑÑ Ð²Ð²ÐµÑÑ
по ÑÑÐµÐºÑ Python и пÑиведÑÑ Ðº пÑеÑÑÐ²Ð°Ð½Ð¸Ñ Ð²Ñей ÑÑнкÑии Ñ Ð¾Ñибкой Postgres Pro, Ñак ÑÑо в ÑаблиÑÑ operations запиÑÑ Ð½Ðµ добавиÑÑÑ. ÐÐµÐ½ÐµÐ´Ð¶ÐµÑ ÐºÐ¾Ð½ÑекÑÑа подÑÑанзакÑии не пеÑеÑ
ваÑÑÐ²Ð°ÐµÑ Ð¾Ñибки, он ÑолÑко гаÑанÑиÑÑеÑ, ÑÑо вÑе опеÑаÑии Ñ Ð±Ð°Ð·Ð¾Ð¹ даннÑÑ
в его облаÑÑи дейÑÑÐ²Ð¸Ñ Ð±ÑдÑÑ Ð°ÑомаÑно заÑикÑиÑÐ¾Ð²Ð°Ð½Ñ Ð¸Ð»Ð¸ оÑмененÑ. ÐÑÐºÐ°Ñ Ð±Ð»Ð¾ÐºÐ° подÑÑанзакÑии пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð¿Ñи иÑклÑÑении лÑбого вида, а не ÑолÑко иÑклÑÑениÑ, вÑзванного оÑибками пÑи обÑаÑении к базе даннÑÑ
. ÐбÑÑное иÑклÑÑение Python, вÑзванное внÑÑÑи блока Ñвной подÑÑанзакÑии, Ñакже пÑиведÑÑ Ðº оÑкаÑÑ ÑÑой подÑÑанзакÑии.