При использовании внешней компоненты 1SQLite в среде 1С:Предприятия 7.7 разработчики нередко сталкиваются с ситуацией, когда после обновления версии библиотеки с 1.0.2.3 или 1.0.2.4 на 1.0.2.6 перестают корректно отрабатывать условия точного равенства по полям документов или справочников. При этом частичный поиск через LIKE продолжает возвращать строки, а попытка вернуть прежнюю версию компоненты приводит к ошибке «file is encrypted or is not a database». Разберем детально, почему это происходит и как правильно решить возникшие проблемы.
Рассмотрим типовой сценарий записи и последующего поиска ссылки на документ, в котором проявляется проблема:
ЗапросSQLite.Подставлять("ТекВремя", ТекущееВремя());
ЗапросSQLite.Подставлять("ТекДата", ТекущаяДата());
ЗапросSQLite.Подставлять("ТекДок", Док);
ТекстЗапроса = "
|INSERT INTO Sklad (time, date, given, doc) VALUES (:ТекВремя, :ТекДата, 0, :ТекДок);
|";
ЗапросSQLite.Выполнить(ТекстЗапроса);
// Попытка проверки записи
ЗапросSQLite.Подставлять("НачДатаПров", ТекущаяДата() - 45);
ЗапросSQLite.Подставлять("КонДатаПров", ТекущаяДата());
ЗапросSQLite.Подставлять("ПроверДок", Док);
ТекстЗапросаПров = "
|SELECT
| Sklad.doc as [Реализация $Документ.Реализация]
|FROM
| Sklad
|WHERE
| (Sklad.date between :НачДатаПров and :КонДатаПров)
| and (Sklad.doc = :ПроверДок)
|";
Выясним причину, по которой условие (Sklad.doc = :ПроверДок) перестает находить данные в новой версии движка. Причина кроется в определении структуры таблицы и механизме типизации (Type Affinity) в SQLite:
IDDOC, ID) представляют собой строки в 36-ричной системе счисления длиной до 9 символов, дополненные пробелами. Например: ' 1E600 ' или ' 1E4R6 '.doc INTEGER, движок SQLite применяет к нему числовое сродство (INTEGER affinity). При вставке строковых значений SQLite не блокирует операцию, но при выполнении операторов сравнения (=, <, >) предпринимает попытку преобразовать обе стороны выражения в числовой тип.' 1E600 ' присутствуют цифры и латинская литера E. Начиная с версии движка SQLite, используемой в 1SQLite 1.0.2.6, расширен парсер распознавания чисел. Строка 1E600 воспринимается парсером как число с плавающей точкой в научной нотации (1, умноженное на 10 в 600-й степени). Так как стандарт IEEE 754 (double precision) ограничен значением порядка 1.79E+308, происходит переполнение до бесконечности (+Infinity), и операция сравнения на точное равенство всегда возвращает ложь. Идентификаторы вида ' 1E4R6 ' не могут быть преобразованы в число из-за буквы R, поэтому остаются строками и сравниваются без сбоев.Sklad.doc LIKE '%'||:ПроверДок||'%' работает даже при типе INTEGER, поскольку оператор LIKE принудительно преобразует операнды к строковому представлению (TEXT affinity), полностью игнорируя попытки распарсить число с плавающей точкой.Главным и фундаментально правильным решением является изменение типа колонки с идентификатором. Все колонки, предназначенные для хранения ссылок 1С, обязаны иметь текстовый тип.
TEXT, VARCHAR или CHAR(9):
CREATE TABLE Sklad (
time TEXT,
date TEXT,
given INTEGER,
doc CHAR(9)
);
CAST:
SELECT
Sklad.doc as [Реализация $Документ.Реализация]
FROM
Sklad
WHERE
(Sklad.date between :НачДатаПров and :КонДатаПров)
and (CAST(Sklad.doc AS TEXT) = CAST(:ПроверДок AS TEXT));
CREATE INDEX idx_Sklad_doc ON Sklad (doc COLLATE _1C);
Сравнение с COLLATE _1C игнорирует различия в лидирующих пробелах при строковом сопоставлении ID 1С.Если после перехода на 1SQLite 1.0.2.4 или 1.0.2.6 потребовалось вернуться на версию 1.0.2.3, старая версия компоненты не сможет открыть базу данных и выдаст сообщение о повреждении или шифровании файла. Это связано с тем, что новый движок SQLite переводит заголовок файла в режим поддержки расширенных возможностей (например, WAL-журналирования), устанавливая в байтах смещения 0x12 и 0x13 заголовка значение 0x02 вместо 0x01.
Рассмотрим два способа возврата совместимости файла базы данных:
ЗапросSQLite.Выполнить("PRAGMA journal_mode = DELETE;");
Команда переводит журнал в классический режим и автоматически сбрасывает флаги версии формата в заголовке файла в значение 1.0x12 и 0x13 (18-й и 19-й байты от начала файла) и изменяем их значения с 02 на 01. После сохранения изменений библиотека 1SQLite версии 1.0.2.3 сможет успешно открыть файл.Падение скорости выполнения запросов после обновления движка SQLite обычно вызвано изменением логики оптимизатора запросов (Next Generation Query Planner). При соединении таблиц 1С с локальными таблицами SQLite оптимизатор без явных индексов может ошибочно выбирать полное сканирование (Full Scan) вместо поиска по индексу.
Проанализируем шаги для восстановления высокой скорости работы:
JOIN и секциях WHERE:
CREATE INDEX idx_Sklad_date_doc ON Sklad (date, doc);
CROSS JOIN вместо INNER JOIN. В SQLite оператор CROSS JOIN жестко предписывает движку обходить таблицы строго в том порядке, в котором они перечислены в тексте запроса.
WHERE +Sklad.doc = :ПроверДок
Это выражение лишает оптимизатор возможности использовать индекс по полю doc, если он приводит к деградации плана выполнения.