Переключение на Главную Страницу Страницы: 1 ОтправитьПечать
Обычная тема баг ВыполнитьSQL_ИзТЗ / icpp 3.2.4.3 (число прочтений - 1498 )
7up
YaBB Newbies
*
Отсутствует


1C++ rocks!

Сообщений: 4
Зарегистрирован: 18. Сентября 2015
Пол: Мужской
баг ВыполнитьSQL_ИзТЗ / icpp 3.2.4.3
08. Июля 2019 :: 08:51
Печать  
Доброго времени суток, Динозавры77.

Наступил на неприятную вещь, делюсь как обойти/исправить.
При повторном вызове ВыполнитьSQL_ИзТЗ без повторного Подготовить данные неверно передаются на сервер, возможны варианты:
а) фантомные строки
б) вылет платформы с ошибкой доступа к памяти

Воспроизводится так:

Процедура Сформировать()
     рс = СоздатьОбъект("ODBCRecordSet");
     рс.ВыполнитьСкалярный("drop table if exists #tbl");
     рс.ВыполнитьИнструкцию("create table #tbl(k1 char(9), k2 char(9))");
     рс.Подготовить("insert #tbl(k1,k2) values(?,?)");
     тз = СоздатьОбъект("ТаблицаЗначений");
     тз.НоваяКолонка("к1","Строка");
     тз.НоваяКолонка("к2","Строка");
     
     Для ъъ=1 По 100 Цикл
           тз.НоваяСтрока();
           тз.к1 = ъъ;
           тз.к2 = 100 - ъъ;
     КонецЦикла;
     рс.ВыполнитьSQL_ИзТЗ(тз);
     
     тз.УдалитьСтроки();
     Для ъъ=1 По 99 Цикл
           тз.НоваяСтрока();
           тз.к1 = ъъ;
           тз.к2 = 99 - ъъ;
     КонецЦикла;
     рс.ВыполнитьSQL_ИзТЗ(тз);
     
     тз1 = рс.ВыполнитьИнструкцию("select * from #tbl");
     тз1.ВыбратьСтроку();
     
КонецПроцедуры

В результате в выборке будут фантомные строки, оставшиеся в памяти от первого заполнения таблицы значений.
Ошибка кроется в Odbccore.cpp,
BOOL CODBCRecordset::SetRowsetSizeForParams(int nRows)

Повторный вызов функции не выделяет память, если выборка открыта (подготовлена). Вторая таблица значений содержит меньше строк, соответственно, пакет в 10% от таблицы значений содержит не 10, а 9 строк. Однако память для передачи параметров не перераспределяется, а используется прежний буфер в 10 строк, в котором 10-я строка содержит фантомные значения.

Обойти можно, повторно вызвав Подготовить перед вторым ВыполнитьSQL_ИзТЗ, но в моем случае это создавало лишнюю нагрузку на сервер (~миллионы вызовов по сотне строк в # - sp_unprepare / sp_prepare)

Исправляется тут (возможно, не самый лучший вариант багфикса, но можно сделать без пересборки с исходников):
//      if (!m_bIsOpen && m_nRowsetSizeForParams != nRows)
     if (m_nRowsetSizeForParams != nRows)

или в бинарнике (1cpp.dll):
0010C0F1: 75 4E 3B 6E 5C 74 49 8B  46 28 85 C0 74 08 50 FF
->
0010C0F1: 90 90 3B 6E 5C 74 49 8B  46 28 85 C0 74 08 50 FF

Глядишь, еще живы те, кто могут пересобрать сие чудо++ и включить этот багфикс))
  
Наверх
 
IP записан
 
trad
1c++ power user
1c++ donor
1c++ moderator
Отсутствует



Сообщений: 3046
Местоположение: Киров
Зарегистрирован: 23. Мая 2006
Пол: Мужской
Re: баг ВыполнитьSQL_ИзТЗ / icpp 3.2.4.3
Ответ #1 - 08. Июля 2019 :: 13:43
Печать  
интересно, а так производительность сильно просядет?
рс.ВыполнитьSQL_ИзТЗ(тз, 1);
  

1&&2&&3
Наверх
 
IP записан
 
7up
YaBB Newbies
*
Отсутствует


1C++ rocks!

Сообщений: 4
Зарегистрирован: 18. Сентября 2015
Пол: Мужской
Re: баг ВыполнитьSQL_ИзТЗ / icpp 3.2.4.3
Ответ #2 - 09. Июля 2019 :: 07:48
Печать  
trad писал(а) 08. Июля 2019 :: 13:43:
рс.ВыполнитьSQL_ИзТЗ(тз, 1);

Заметно проседает:

     тз = СоздатьОбъект("ТаблицаЗначений");
     тз.НоваяКолонка("к1","Строка");
     тз.НоваяКолонка("к2","Строка");
     Для ъъ=1 По 100000 Цикл
           тз.НоваяСтрока();
           тз.к1 = ъъ;
           тз.к2 = 100000 - ъъ;
     КонецЦикла;
     рс = СоздатьОбъект("ODBCRecordSet");

     рс.ВыполнитьСкалярный("drop table if exists #tbl");
     рс.ВыполнитьИнструкцию("create table #tbl(k1 char(9), k2 char(9))");
     рс.Подготовить("insert #tbl(k1,k2) values(?,?)");
     нач = _GetPerformanceCounter();
     рс.ВыполнитьSQL_ИзТЗ(тз);
     время = _GetPerformanceCounter() - нач;
     Сообщить(время);
     // SQL+1С на одном сервере 7302
     // SQL и 1С на разных серверах  7728


     рс.ВыполнитьСкалярный("drop table if exists #tbl");
     рс.ВыполнитьИнструкцию("create table #tbl(k1 char(9), k2 char(9))");
     рс.Подготовить("insert #tbl(k1,k2) values(?,?)");
     нач = _GetPerformanceCounter();
     рс.ВыполнитьSQL_ИзТЗ(тз, 1);
     время = _GetPerformanceCounter() - нач;
     Сообщить(время);
     // SQL +1С на одном сервере 20563
     // SQL и 1С на разных серверах  65432
  
Наверх
 
IP записан
 
Ветер в поле
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 40
Зарегистрирован: 06. Октября 2010
Пол: Мужской
Re: баг ВыполнитьSQL_ИзТЗ / icpp 3.2.4.3
Ответ #3 - 11. Июня 2020 :: 09:02
Печать  
Я с таким багом не сталкивался, т.к. не было необходимости в повторном использовании.
Но от процедуры ВыполнитьSQL_ИзТЗ вообще отказался, т.к. в некоторых случаях (закономерность отследить не удалось) на каком-то этапе выполнение прекращалось без каких-либо ошибок. Т.е. ты думаешь, что у тебя всё записалось в базу, а на самом деле обработано было только 20% таблицы.
Пришлось написать процедуру глВыполнитьSQL_ИзТЗ, которая делает то же самое, что и ВыполнитьSQL_ИзТЗ(ТЗ, 1). Да, медленно, но верно.
  
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: 1
ОтправитьПечать