Я работаю над некоторым кодом C ++, который использует libpq для взаимодействия с базой данных PostgreSQL, и я начал писать некоторые функции, каждая из которых внутренне запускает транзакцию, применяет несколько изменений к БД, а затем завершает транзакцию.
Теперь я хотел бы вызвать одну из этих функций в сочетании с другим оператором DML, и все они выполняются в одной транзакции. Примерно так (примечание: очень упрощенно):
void doFoo(PGconn* con) {
PQexec(con, "BEGIN" );
PQexec(con, "insert into ..." );
PQexec(con, "delete from ..." );
PQexec(con, "END" );
}
void doFooPlus(PGconn* con) {
PQexec(con, "BEGIN" );
doFoo(con);
PQexec(con, "update ..." );
PQexec(con, "END" );
}
void main(void) {
doFooPlus(con);
}
Однако, судя по всему, что я прочитал, похоже, что PostgreSQL может не учитывать такого рода вложенность транзакций. Я хочу внести ясность: мне не нужны автономные транзакции, которые, как я знаю, PostgreSQL не поддерживает, и мне не нужны какие-либо явные функции отката к вложенному (или другому) оператору BEGIN, который может быть выполнен с помощью точек сохранения, и очевидно, это не то, что приведенный выше код пытается сделать в любой момент. Я просто хочу подтвердить, будет ли приведенный выше код делать то, что можно было бы надеяться, исходя из структуры кода.
Позвольте мне попытаться уточнить дальше. Вот что PostgreSQL в конечном итоге увидит из приведенного выше кода C ++ (ну, на самом деле, просто C):
BEGIN
BEGIN
insert into ...
delete from ...
END
update ...
END
Что меня беспокоит, так это то, что второй вызов BEGIN полностью игнорируется, и поэтому первый вызов END завершит транзакцию, начатую первым вызовом BEGIN, и поэтому оператор обновления не будет включен в атомарности блока транзакции.
Согласно http://www.postgresql.org/docs/9.4/static/sql-begin.html:
Выдача BEGIN уже внутри блока транзакции вызовет предупреждающее сообщение. На состояние транзакции это не влияет. Чтобы вложить транзакции в блок транзакции, используйте точки сохранения (см. SAVEPOINT).
Комментарий о точках сохранения кажется мне вводящим в заблуждение; PostgreSQL не поддерживает вложенные (автономные) транзакции; точки сохранения предоставляют только способ отката к точке в середине существующей транзакции. Точки сохранения сами по себе не являются транзакциями, поэтому я не могу заменить ими BEGIN и END в doFoo()
, потому что тогда я не смогу вызвать doFoo()
сам по себе (то есть не из doFooPlus()
) и все равно получить атомарность транзакции для вставки и удаления, выполняемой doFoo()
.
Комментарий о состоянии транзакции «не [не] затронутой» вложенным BEGIN, похоже, подразумевает, что PostgreSQL не будет ее считать и фактически полностью проигнорирует, но цитируемая фраза не совсем < / em> проясни мне, что я не собирался спрашивать об этом в Stack Overflow. Я все еще держу унцию надежды, что PostgreSQL по-прежнему будет считать вложенный BEGIN на какой-то внутренний «уровень вложенности», который будет уменьшаться первым END, а затем снова уменьшаться вторым END, вызывая всю последовательность операторов, которые будут рассматриваться как одна атомарная транзакция.
Итак, может кто-нибудь подтвердить, делает ли PostgreSQL это или нет? А если нет, не могли бы вы дать рекомендации, как лучше всего решить эту проблему в моем коде? Я думал о добавлении параметра bool, чтобы позволить вызывающему doFoo()
указать, создавать ли транзакцию или нет, которой doFooPlus()
может передать false.
Изменить: для всех, кому интересно, сегодня я понял, что могу довольно легко проверить этот вопрос сам, просто написав программу, которая делает что-то вроде того, что пытается сделать приведенный выше пример кода, а затем изучив ее влияние на базу данных.
Я не буду вдаваться в подробности о внутреннем устройстве программы, но приведенная ниже команда в основном запускает create table t1 (a int, b int, c int ); insert into t1 (a,b,c) values (0,0,0);
, затем запускает каждый из заданных операторов SQL по порядку и, наконец, печатает результирующие данные таблицы, так что вы можете видеть, что второй begin
и последний rollback
были полностью проигнорированы:
> pgtestabc begin begin 'update t1 set a=1;' 'update t1 set b=1;' end 'update t1 set c=1;' rollback;
executing "begin"...done
executing "begin"...done
executing "update t1 set a=1;"...done
executing "update t1 set b=1;"...done
executing "end"...done
executing "update t1 set c=1;"...done
executing "rollback"...done
1|1|1
Также обратите внимание, что вы не можете выполнить этот точный тест, просто запустив операторы SQL в пакете из клиента с графическим интерфейсом, такого как pgAdmin III. Этот конкретный клиент, кажется, творит чудеса с транзакциями; кажется, пакет заключен в неявную транзакцию, так что оператор rollback
вызовет откат предыдущих операторов (даже если вы также получаете сообщение «ВНИМАНИЕ: транзакция не выполняется» на панели сообщений ... ), за исключением того, что он также каким-то образом уважает _14 _..._ 15_ блоков (игнорируя вложенные begin
операторы, как показано выше) в пакете, что до смешения кажется ужасно похожим на автономные транзакции, которые postgres не поддерживает, поскольку Я считаю, что мы установили в этой теме. Так, например, если вы запустите указанные выше 7 операторов непосредственно в pgAdmin III, вы получите данные 1|1|0
.
Но независимо от этой непреднамеренной обфускации, неопровержимый вывод состоит в том, что postgres не считает уровни вложенности _18 _..._ 19_ блоков, поэтому вы должны позаботиться о том, чтобы когда-либо помещать себя только на один верхний уровень begin
. .._ 21_ блок.
doFoo()
и получить обычную (не подпрограмму) транзакцию, но я также хочу иметь возможность вызыватьdoFooPlus()
и получать всю последовательность операторов, включаяdoFoo()
, в атомарной транзакции. Произойдет ли это так, как я написал пример кода в моем вопросе? - person bgoldst   schedule 11.01.2015