Собственно вопрос в теме...
1. Делаем start transaction; .
2. делаем insert который вылетает по причине ..... не важно пусть будет FOREIGN KEY
-> Текущая транзакция не валидна....
Как проверить валидность текущей транзакции?
Приложение на C++, QT 4.7. Но это не главное. Просто вообще, есть ли такая возможность и любой вариант достучаться до нее.
Транзакции работают по
Транзакции работают по принципу всё или ничего.
Когда вы сделаете COMMIT, то ВСЕ операторы в транзакции где произошёл сбой выполнены не будут.
Т.е. состояние БД не изменится, как будто этой транзакции не было.
Отсюда напрашивается самый простой способ проверки - проверяем есть ли в БД те изменения, которые должна внести транзакция. Если их нет, значит транзакция невалидна.
Разумеется в C++ как и в других языках программирования есть коды возврата при выполнении операторов SQL, а также возможности спросить СУБД о том какая ошибка случилась. Ищите в документации.
Все выше перечисленное и так
Все выше перечисленное и так понятно.
С++ к общению с postgresql отношения никакого не имеет - имеют библиотеки.
Это для начала.
На самом деле есть следующая задача где нужно проверить существующую транзакцию:
У пользователя открывается диалог на добавление/редактирование записи в транзакции. Далее он открывает на связывание другую таблицу из которой выбирает.
Но тут он обнаруживает, что в таблицу выбора надо добавить новый элемент и он реально хочет его добавить...... Нам надо узнать, существует ли уже открытая транзакция и надо ли в данном случае запускать транзакцию или устанавливать SAVEPOINT...
----------------------------
Что-то типа такого.... Сложно у меня с описанием.
т.е. надо понять существует ли начатая, не автоматическая и живая транзакция.
Можно через счетчик пропустить(т.е. начинаем транзакцию - 1; закрываем - 0). тогда возникнет проблема оценки ее на предмет 'живая'.
Тестовая запись в базу - вариант, и даже будет работать, - но решение в принципе кривое. т.е. сеть и сервер больше занять не чем, чем для выяснения гонять 3 запроса(insert, select, delete) + 3 ответа на каждый запрос.
Я раcчитывал на более простое решение, типа выборки из системных таблиц или похожее.
PS: В документации подобного не нашел, и google не помог... Иначе не писал бы.
> Все выше перечисленное и
> Все выше перечисленное и так понятно.
Видимо не очень понятно. Транзакция не видна никаким другим транзакциям и операторам до тех пор пока она не завершится. Поэтому проверить наличие работы другой транзакции не представляется возможным - она просто не будет видна, как будто её нет.
Прочитав ваше описание я склоняюсь к мнению о неверной архитектуре вашей системы, потому что надо сперва подготовить все данные, а затем уже запускать транзакцию. Открывать транзакцию, а затем на полчаса запускать диалог пользователя, который может быть на обед сходит прежде чем этот диалог заполнит - право не стоит.
Если уж вам никак по другому не получается - используйте блокировки таблиц, но в вашем случае это химера, потому что пока таблица не разблокирована другие пользователи будут обламываться.
Может быть я чего-то недопонимаю, но тогда описывайте подробно что к чему
> Все выше перечисленное и
> Все выше перечисленное и так понятно.
> Видимо не очень понятно. Транзакция не видна никаким другим транзакциям и >операторам до тех пор пока она не завершится. Поэтому проверить наличие
>работы другой транзакции не представляется возможным - она просто не будет >видна, как будто её нет.
Неет.
Все той-же транзакции.
> Прочитав ваше описание я склоняюсь к мнению о неверной архитектуре вашей
> системы, потому что надо сперва подготовить все данные, а затем уже запускать > транзакцию.
И зачем же тогда версионная система....
Если все подряд загонять одной короткой транзакцией.
Ценность подобного нивелируется.
К тому-же у транзакций существует, и не зря, уровень изоляции.
> Открывать транзакцию, а затем на полчаса запускать диалог пользователя,
> который может быть на обед сходит прежде чем этот диалог
> заполнит - право не стоит.
Однозначно, но если это происходит в транзакции, что не мешает другим пользователям просматривать/редактировать записи, в том числе и редактируемую в транзакции. Проблем не возникает, собственно для этого транзакции и нужны.
>Если уж вам никак по другому не получается - используйте блокировки таблиц, но >в вашем случае это химера, потому что пока таблица не разблокирована другие >пользователи будут обламываться.
Нет!
> Может быть я чего-то недопонимаю, но тогда описывайте подробно что к чему.
1. Базу пользователи смотрят в режиме автоматической транзакции, которая подтверждается сразу после выполнения запроса.
2. Редактирование переводит в режим ручного управления транзакциями и начинает новую транзакцию.
3. Ок-> commit Cancel -> rollback
4. Просмотр (автоматические транзакции)
Но если диалог редактирования выбирает из списка элементов значение для foreign key и при этом необходимо добавить еще значение к списку выбираемых, то вложить транзакцию не получится (postgresql такого не делает), надо использовать SAVEPOINT.
Таким образом: надо понять в каком режиме сейчас находится транзакция, запущенная или автоматическая. Если автоматическая начать новую, если запущена ручная - поставить SAVEPOINT.
Проблема: узнать есть ли запущенная транзакция, и ничего ли там пользователь не на косячил...
Собственно все
Пока решил счетчиком:
if(transactioncount++==0)starttransaction
else SAVEPOINT transactioncount
Ну и соответствующие действия для commit и rollback.
Не знаю к чему такие
Не знаю к чему такие сложности.
Проще при запуске диалога транзакцию не запускать, а запускать её только при ОК, когда в диалоге выбраны уже все необходимые данные. Если вам скажем при открытии диалога нужно зарезервировать какой-либо номер, то опять же вы можете выполнить короткую транзакцию, которая возьмёт последний доступный номер, занесёт его в таблицу зарезервированных и продвинет последний доступный номер на единицу. Вот вам кстати способ понять запущен ли диалог или нет - наличие/отсутствие зарезервированных номеров. После закрытия диалога номер из таблицы зарезервированных стирается. Чтобы вообще было чистенько, можно в таблицу резервных номеров добавлять ещё и время и пользователя, тогда вообще красиво получается - видим все открытые диалоги у всех пользователей и можем подчистить "зависшие" или даже не давать пользователю повторно открыть диалог пока он не закрыл первый.
Чтобы просматривать базу транзакции вообще не нужны, они нужны только при внесении изменений да и то только в случаях комплексных операций, а такие операции как занесение доп. записи в справочник (видимо это вы и подразумеваете под выбором значения для foreign key) могут и без транзакций обойтись.
Другой пример
Есть более простые причины для того, чтобы запросить у базы статус транзакции.
1. Чтобы просматривать базу транзакции вообще не нужны - да, если вы не используете LOCK TABLE при низких уровнях изоляции в настройках СУБД.
2. Пример:
Пусть есть виджет/элемент интерфейса, у которого реализованы два метода: reloadContent() и, собственно конструктор.
reloadContent за минимальное количество действий перегружает из БД только те данные, которые были изменены на стороне БД, либо все данные (в зависимости от флага). При этом reloadContent меняет данные в БД - он обновляет дату их последнего запроса пользователем. Очевидно, конструктор загружает данные в виджет при создании, вызывая (для экономии кода) reloadContent. Если же конструктор желает установить блокировку на уровне записи, он должен обновить эту запись. Получается следующее.
Чтобы просто обновить данные:
- reloadContent запускает транзакцию и сама её закрывает.
А чтобы инициализировать виджет:
- конструктор открывает транзакцию, после чего вызывает reloadContent.
- повторный вызов транзакции из reloadContent в PostgreSQL не приводит к "вложенной транзакции" - и это логично.
- при завершении reloadContent будет закрыта транзакция конструктора - что нарушает логику.
Понятно, что желательно в reloadContent проверить, не была ли уже открыта транзакция, и принять решение о том, закрывать ли её. Но драйвер БД такой возможности не предоставляет.
Есть решение - передавать в reloadContent лишний флаг, но это оставляет возможности ошибки для программиста, вызывающего функцию reloadContent. Кроме того, я на практике уже столкнулся с трёхкратной вложенностью. Решал отдельным глобальным счётчиком "вложенности" транзакций, со своими transaction(), rollback() и commit().
И хотя reloadContent можно на две части, ("private запрос_из_БД_без_транзакции" и вызывающий его "public обновить()"), с конструктором такой фокус уже не пройдёт. Класс получается небезопасным/не универсальным только потому, что он может "ненароком" закрыть чужую транзакцию.
P.S.:
Есть возможность проверить запросом к метаданным, если не брезгуете лазить в pg_catalog
http://gray-hemp.blogspot.ru/2010/03/blog-post_26.html
Подходит :)
Поздновато для того проекта для которого спрашивал.
Но в архивчик себе закинул )
Через pg_catalog прекрасное решение. Просто в свое время не нашел.
Спасибо за подсказку.
А я бы не стал использовать
А я бы не стал использовать такое решение. Хотя бы потому, что оно не кроссплатформенное и на другой СУБД работать не будет.