Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,8 @@

Например, `КоннекторJSON` не умеет работать с транзакциями, однако, он имеет соответствующие методы, выводящие диагностические сообщения при их вызове.

Коннектор с транзакциями **обязан** сам знать, открыта ли транзакция, и отвечать на это методом `ВТранзакции()`: транзакцию может начать текстом запроса и бросить алгоритм `ВыполнитьСКоннектором`, и пул откатывает ее по этому ответу, прежде чем соединение достанется другому потоку. Коннектор без транзакций отвечает `Ложь`.

Коннектор **может** дополнительно реализовать интерфейс [`НаблюдаемыйКоннектор`](docs/НаблюдаемыйКоннектор.md): тогда наблюдатели источника данных получают события его запросов к СУБД и описание соединения. Встроенные коннекторы реализуют оба интерфейса. Коннектор без второго интерфейса работает как раньше.

> Важно!
Expand Down
19 changes: 19 additions & 0 deletions docs/АбстрактныйКоннектор.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@
Процедура ОтменитьТранзакцию() Экспорт
```

### ВТранзакции

Коннектор с транзакциями ведет признак сам: транзакцию может начать и бросить алгоритм
`ВыполнитьСКоннектором` мимо библиотеки, и узнать о ней пул может только у коннектора. Коннектор
без транзакций отвечает `Ложь`.

```bsl
// Определяет, начата ли этим коннектором транзакция, которая еще не завершена: методом
// коннектора или текстом запроса прямого доступа.
//
// По ответу пул откатывает транзакцию, брошенную алгоритмом ВыполнитьСКоннектором, прежде чем
// соединение достанется другому потоку исполнения. Коннектор без транзакций отвечает Ложь.
//
// Возвращаемое значение:
// Булево - Истина, если транзакция начата и не завершена.
//
Функция ВТранзакции() Экспорт
```

### ИнициализироватьТаблицу

```bsl
Expand Down
11 changes: 11 additions & 0 deletions docs/КоннекторJSON.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@
Процедура ОтменитьТранзакцию() Экспорт
```

### ВТранзакции

```bsl
// Определяет, начата ли транзакция. Транзакций коннектор не поддерживает.
//
// Возвращаемое значение:
// Булево - Всегда Ложь.
//
Функция ВТранзакции() Экспорт
```

### ИнициализироватьТаблицу

```bsl
Expand Down
12 changes: 12 additions & 0 deletions docs/КоннекторSQLite.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@
Процедура ОтменитьТранзакцию() Экспорт
```

### ВТранзакции

```bsl
// Определяет, начата ли этим коннектором транзакция, которая еще не завершена: методом
// коннектора или текстом запроса.
//
// Возвращаемое значение:
// Булево - Истина, если транзакция начата и не завершена.
//
Функция ВТранзакции() Экспорт
```

### ИнициализироватьТаблицу

```bsl
Expand Down
26 changes: 26 additions & 0 deletions docs/ПотокобезопаснаяРаботаСБД.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,32 @@ PostgreSQL это закрывает параметр строки соедин
Если текущий контекст исполнения начал транзакцию, алгоритм получает коннектор этой транзакции и
попадает в нее.

Транзакцию для алгоритма открывайте менеджером: `МенеджерСущностей.НачатьТранзакцию()` до
`ВыполнитьСКоннектором`, фиксация или отмена - после. Транзакция, которую алгоритм начал сам -
методом коннектора или запросом `BEGIN`, - и не завершил, откатывается при возврате соединения в
пул: иначе она ушла бы с соединением к другому контексту, и его фиксация записала бы строки
алгоритма. Наблюдатели увидят запрос `ROLLBACK`, события транзакции у нее нет: начинала ее не
библиотека.

```bsl
// Удаление не применится: транзакция брошена алгоритмом и откатывается при возврате соединения
МенеджерСущностей.ВыполнитьСКоннектором(
"Коннектор -> Коннектор.ВыполнитьЗапрос(""BEGIN""); Коннектор.ВыполнитьЗапрос(Текст)",
Новый Структура("Текст", "DELETE FROM Авторы")
);
```

О транзакции коннектор узнает по первому оператору каждого выполненного запроса: `BEGIN` и
`START TRANSACTION` ее начинают, `COMMIT`, `END` и `ROLLBACK` завершают, откат к точке сохранения
(`ROLLBACK TO`) не завершает. Не видны ему транзакции, начатые или завершенные вторым оператором
многооператорного текста, процедурой на стороне СУБД, а в SQLite - еще и `SAVEPOINT` вне транзакции
и `RELEASE` последней точки сохранения. О транзакции пулу сообщает сам коннектор методом
`ВТранзакции()` интерфейса `АбстрактныйКоннектор`: сторонний коннектор с транзакциями должен вести
этот признак сам.

Коннектор не храните вне алгоритма: после выхода из него соединение принадлежит пулу и может
работать в другом контексте.

Прежний `ПолучитьКоннектор()` удален: он отдавал соединение наружу, не имея способа получить его
обратно, поэтому соединение оставалось за контекстом навсегда.

Expand Down
28 changes: 25 additions & 3 deletions src/internal/Классы/СоединениеСБД.os
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@
// Отмечает возврат соединения в пул.
//
// Незавершенная транзакция при этом откатывается: соединение уходит следующему потоку, и
// оставлять на нем чужую начатую транзакцию нельзя. Ошибка отката наружу не идет - соединение
// возвращается в пул в любом случае, иначе оно было бы потеряно, - но соединение после нее
// непригодно так же, как после неудачного явного завершения транзакции.
// оставлять на нем чужую начатую транзакцию нельзя. Откатывается и транзакция, которую начал и
// бросил алгоритм прямого доступа мимо этого соединения: о ней знает коннектор. Ошибка отката
// наружу не идет - соединение возвращается в пул в любом случае, иначе оно было бы потеряно, - но
// соединение после нее непригодно так же, как после неудачного явного завершения транзакции.
//
Процедура ОтметитьВозврат() Экспорт

Expand All @@ -227,6 +228,8 @@
КонецПопытки;
ВТранзакции = Ложь;
ЗавершитьВерхнееСобытиеТранзакции("abandoned", ОшибкаОтката);
Иначе
ОткатитьТранзакциюКоннектора();
КонецЕсли;

// Остаток стопки - события начал, которым не досталось своего завершения: их закрывает
Expand All @@ -239,6 +242,25 @@

КонецПроцедуры

// Откатывает транзакцию, которую начал мимо этого соединения и не завершил алгоритм прямого
// доступа.
//
// Начинала ее не библиотека, поэтому события транзакции у нее нет: наблюдатели увидят только
// запрос отката. Если коннектор не смог ответить или откатить, состояние транзакции неизвестно и
// соединение непригодно.
//
Процедура ОткатитьТранзакциюКоннектора()

Попытка
Если Коннектор.ВТранзакции() Тогда
РаботаСКоннекторами.ОтменитьТранзакцию(Коннектор);
КонецЕсли;
Исключение
ИспортитьПослеНеудачногоЗавершения();
КонецПопытки;

КонецПроцедуры

// Закрывает соединение.
//
Процедура Закрыть() Экспорт
Expand Down
15 changes: 14 additions & 1 deletion src/Классы/АбстрактныйКоннектор.os
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,22 @@
// Отменяет открытую транзакцию в БД.
//
Процедура ОтменитьТранзакцию() Экспорт

КонецПроцедуры

// Определяет, начата ли этим коннектором транзакция, которая еще не завершена: методом
// коннектора или текстом запроса прямого доступа.
//
// По ответу пул откатывает транзакцию, брошенную алгоритмом ВыполнитьСКоннектором, прежде чем
// соединение достанется другому потоку исполнения. Коннектор без транзакций отвечает Ложь.
//
// Возвращаемое значение:
// Булево - Истина, если транзакция начата и не завершена.
//
Функция ВТранзакции() Экспорт
Возврат Ложь;
КонецФункции

// Создает таблицу в БД по данным модели.
//
// Параметры:
Expand Down
88 changes: 85 additions & 3 deletions src/Классы/АбстрактныйКоннекторSQL.os
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
// НаблюдателиИсточникаДанных - Наблюдатели пула, которым сообщается о каждом запросе. Неопределено,
// пока коннектор работает вне пула.
Перем Наблюдатели;
// Булево - Транзакция начата запросом этого коннектора и еще не завершена, см. ОтметитьТранзакцию.
// Соединение занято одним потоком исполнения, поэтому признак читает и пишет только он.
Перем ТранзакцияОткрыта;

Процедура ПриСозданииОбъекта(пКоннектор, пЛог = Неопределено)

Коннектор = пКоннектор;
ТранзакцияОткрыта = Ложь;
Если ЗначениеЗаполнено(пЛог) Тогда
Лог = пЛог;
Иначе
Expand Down Expand Up @@ -59,6 +63,7 @@
ВызватьИсключение;
КонецПопытки;

ОтметитьТранзакцию(ОперацияСУБД, Запрос.Текст);
ЗавершитьЗапрос(Событие, , ?(ВернутьРезультат, Результат.Количество(), Неопределено));

Возврат Результат;
Expand All @@ -85,6 +90,83 @@

КонецПроцедуры

// Определяет, начата ли запросом этого коннектора транзакция, которая еще не завершена.
//
// Признак знает и о транзакции, начатой мимо библиотеки: методом коннектора или текстом запроса
// в прямом доступе. Пул по нему откатывает транзакцию, брошенную алгоритмом, прежде чем отдать
// соединение другому потоку исполнения.
//
// Возвращаемое значение:
// Булево - Истина, если транзакция начата и не завершена.
//
Функция ВТранзакции() Экспорт
Возврат ТранзакцияОткрыта;
КонецФункции

// Отмечает начало или завершение транзакции успешно выполненным запросом.
//
// Начало - BEGIN и START TRANSACTION, завершение - COMMIT, END и ROLLBACK; откат к точке
// сохранения транзакцию не завершает. Неудавшийся запрос признак не меняет: в каком состоянии
// осталась транзакция, неизвестно, и лишний откат при возврате соединения в пул безопаснее, чем
// пропущенный.
//
// Признак ведется по первому оператору текста. Транзакцию, завершенную вторым оператором
// многооператорного текста или процедурой на стороне СУБД, он не видит.
//
// Параметры:
// ОперацияСУБД - Строка - Операция выполненного запроса.
// ТекстЗапроса - Строка - Текст выполненного запроса.
//
Процедура ОтметитьТранзакцию(Знач ОперацияСУБД, Знач ТекстЗапроса)

Если ОперацияСУБД = "BEGIN" ИЛИ ОперацияСУБД = "START" Тогда
ТранзакцияОткрыта = Истина;
ИначеЕсли ОперацияСУБД = "COMMIT" ИЛИ ОперацияСУБД = "END" Тогда
ТранзакцияОткрыта = Ложь;
ИначеЕсли ОперацияСУБД = "ROLLBACK" И НЕ ЭтоОткатКТочкеСохранения(ТекстЗапроса) Тогда
ТранзакцияОткрыта = Ложь;
КонецЕсли;

КонецПроцедуры

// Определяет, откатывает ли ROLLBACK к точке сохранения: ROLLBACK [WORK | TRANSACTION] TO ...
//
// Параметры:
// ТекстЗапроса - Строка - Текст запроса ROLLBACK.
//
// Возвращаемое значение:
// Булево - Истина, если откат к точке сохранения.
//
Функция ЭтоОткатКТочкеСохранения(Знач ТекстЗапроса)

Слова = СловаЗапроса(ТекстЗапроса);

НомерСлова = 1;
Если Слова.Количество() > НомерСлова И (Слова[НомерСлова] = "WORK" ИЛИ Слова[НомерСлова] = "TRANSACTION") Тогда
НомерСлова = НомерСлова + 1;
КонецЕсли;

Возврат Слова.Количество() > НомерСлова И Слова[НомерСлова] = "TO";

КонецФункции

// Разбивает текст запроса на слова в верхнем регистре. Разделители - пробельные символы и точка
// с запятой.
//
// Параметры:
// ТекстЗапроса - Строка - Текст запроса или команды.
//
// Возвращаемое значение:
// Массив из Строка - Слова текста.
//
Функция СловаЗапроса(Знач ТекстЗапроса)

Разделители = " ;" + Символы.Таб + Символы.ПС + Символы.ВК;

Возврат СтрРазделить(ВРег(ТекстЗапроса), Разделители, Ложь);

КонецФункции

// Определяет операцию СУБД по первым словам текста запроса: SELECT, DELETE, CREATE TABLE и т.п.
//
// Параметры:
Expand All @@ -95,15 +177,15 @@
//
Функция ОперацияИзТекста(Знач ТекстЗапроса)

Слова = СтрРазделить(СокрЛП(СтрЗаменить(ТекстЗапроса, Символы.ПС, " ")), " ", Ложь);
Слова = СловаЗапроса(ТекстЗапроса);
Если Слова.Количество() = 0 Тогда
Возврат "";
КонецЕсли;

Операция = ВРег(Слова[0]);
Операция = Слова[0];
ОперацияНадСхемой = Операция = "CREATE" ИЛИ Операция = "DROP" ИЛИ Операция = "ALTER";
Если ОперацияНадСхемой И Слова.Количество() > 1 Тогда
Операция = Операция + " " + ВРег(Слова[1]);
Операция = Операция + " " + Слова[1];
КонецЕсли;

Возврат Операция;
Expand Down
9 changes: 9 additions & 0 deletions src/Классы/КоннекторInMemory.os
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@
ВызватьИсключение "Не поддерживается";
КонецПроцедуры

// Определяет, начата ли транзакция. Транзакций коннектор не поддерживает.
//
// Возвращаемое значение:
// Булево - Всегда Ложь.
//
Функция ВТранзакции() Экспорт
Возврат Ложь;
КонецФункции

// Создает таблицу в БД по данным модели.
//
// Параметры:
Expand Down
9 changes: 9 additions & 0 deletions src/Классы/КоннекторJSON.os
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@
ВызватьИсключение "Не поддерживается";
КонецПроцедуры

// Определяет, начата ли транзакция. Транзакций коннектор не поддерживает.
//
// Возвращаемое значение:
// Булево - Всегда Ложь.
//
Функция ВТранзакции() Экспорт
Возврат Ложь;
КонецФункции

// Создает таблицу в БД по данным модели.
//
// Параметры:
Expand Down
Loading
Loading