SQLite: что работает иначе
SQLite — библиотека, которая пишет один файл: у неё нет сервера, писать одновременно может одно
подключение, точных десятичных типов и типов дат нет, а колонки таблицы она меняет в основном построением
таблицы заново. Каждая строка ниже говорит, что делает hare с частью SQL, которой в SQLite нет или
которую SQLite понимает по-своему. Чего в SQLite нет совсем — MERGE, LATERAL, последовательностей,
построчной защиты и остального, — см. в таблице
возможностей.
Подключения и транзакции
| SQLite |
hare |
| Один пишущий за раз |
hare держит одно соединение на имя подключения и берёт его как из пула: транзакция занимает его до конца, а запрос другой задачи на том же имени подключения ждёт её — см. Снимок пула. Другое подключение к файлу ждёт блокировку SQLite не дольше busy_timeout. |
| Уровни изоляции |
Каждая транзакция идёт на SERIALIZABLE; atomic() принимает любой уровень — см. Уровень изоляции. |
| Транзакции только для чтения |
На время транзакции включается PRAGMA query_only — см. Транзакции только для чтения. |
| Транзакция, прерванная ошибкой |
Неудачная команда оставляет транзакцию рабочей — см. Неудачная команда в транзакции. Прерванная запись откатывает всю транзакцию вместе с точками сохранения, и hare затем считает транзакцию прерванной — см. Транзакции только для чтения и ограничение времени команды. |
Блокировки строк (SELECT ... FOR UPDATE) |
select_for_update() при выполнении запроса бросает UnSupportedError — см. select_for_update(). |
| База в памяти, общая для подключений |
Каждое подключение :memory: — отдельная база: autonomous() и второе имя подключения не видят её таблиц. |
Значения
Имена в кавычках не различают регистр букв ASCII: Name и name обозначают одну колонку. Буквы вне ASCII
различаются, поэтому É и é могут обозначать разные колонки. При проверке объявлений таблиц, колонок и
индексов hare использует это правило сравнения имён.
| SQLite |
hare |
| Точный десятичный тип |
У DecimalField в SQLite нет колонки, а значение Decimal в запросе к SQLite отклоняется до любого SQL — см. DecimalField (SQLite). |
| Типы дат и времени |
Даты, время и даты-время хранятся текстом ISO; усекают, извлекают и сдвигают их функции hare — см. Функции. |
| Диапазоны целых типа колонки |
Любое 64-битное целое помещается в любую целую колонку, поэтому hare проверяет записываемое им значение по диапазону поля. Значение, вычисленное базой, — собственное SQLite, см. Результаты. |
| Отрицательный ноль |
-0.0 у FloatField читается обратно как 0.0. |
NaN |
FloatField отклоняет NaN при записи и как операнд фильтра: SQLite хранит и подставляет его как NULL. |
Нулевой байт в шаблоне LIKE или полнотекстовом запросе |
Шаблон отклоняется: LIKE читает его только до нулевого байта и нашёл бы то, чего в шаблоне нет (Features.cuts_like_pattern_at_null_byte); так же отклоняется текст SearchQuery — и в __search, и в SearchRank, и в SearchHeadline, — который FTS5 читает так же. Нулевой байт в хранимом тексте или значении JSON сохраняется. |
Длинный шаблон LIKE |
SQLite отклоняет шаблон длиннее 50000 байт (SQLITE_MAX_LIKE_PATTERN_LENGTH) — __contains и другие текстовые операторы фильтра с более длинным текстом дают ValidationError. |
Место NULL в сортировке |
NULL сортируется как наименьшее значение, первым по возрастанию — см. Порядок NULL при сортировке. |
| Порядок текста по локали |
Текст сравнивается по кодовым точкам. |
jsonb |
JSONField имеет тип JSON_TEXT; contains и contained_by дают UnSupportedError — в JSON SQLite нет вхождения. hare добавляет смысл своих путей с цифровым сегментом и сравнение дат, а остальное упорядочивает и сравнивает json_extract() SQLite — см. JSON. |
| Нулевой байт в тексте сырого SQL |
Текст execute_sql(), RawSQL и прочего сырого SQL отправляется как написан; драйвер SQLite отклоняет нулевой байт в нём (the query contains a null character). |
Результаты самого SQLite
hare не заставляет SQLite давать результат PostgreSQL. Она проверяет то, что гарантирует сама, —
значение, которое записывает, член перечисления, — пишет формы, которые хранит сама (дату как текст,
путь JSON), и добавляет функцию, которой у SQLite нет; всё
остальное — результат самого SQLite, и он отличается от результата PostgreSQL:
- JSON. Порядок и сравнение пути или всего
JSONField читают значение, которое даёт json_extract()
(строка как есть, true и false как 1 и 0, JSON null как NULL, объект или массив как сжатый
текст; числа раньше текста). Равенство пути сравнивает форму JSON: true и 1 различаются, строка и
число различаются. Весь JSONField равен, когда равен хранимый текст, поэтому порядок ключей и 1
против 1.0 важны. has_key находит только ключ объекта; SQLite до 3.41 отказывает пути пустого ключа
(has_key="" — JSON path error). Значение, прочитанное как текст, Cast() в
текст и Concat() от него — хранимый текст без пробелов. См. JSON.
- Целочисленная арифметика — собственная SQLite:
update(n=F("n") + 1) сохраняет число за
диапазоном поля, а целый результат за 64 битами становится REAL. Целое поле, которому присваивается
дробь (update(n=F("n") * 2.5)), получает целое, которое даёт CAST SQLite, с отброшенной дробной
частью, — колонка INTEGER сохранила бы дробь.
Cast() к числу или логическому — собственный CAST SQLite: к целому он отбрасывает дробную часть,
тогда как PostgreSQL округляет; текст, не являющийся числом, — 0 (' 42 ' — это 42); логическое —
значение, прочитанное как число, истинное, если оно не 0 ('true' — ложь). Приведение к тексту —
собственное для SQLite: float 2.0 с 15 значащими цифрами (с SQLite 3.53 — с 17), логическое 1 или 0.
См. Приведение типов.
- Текст значения.
Concat() колонки склеивает текст, который SQLite даёт значению (логическое 1
или 0, float -3.0); литерал Python — это его текст в Python (логическое true или false,
float через repr()). Текстовый оператор фильтра для колонки логического типа или float
сопоставляет текст SQLite: 1, 0, 2.0. Наивное дата-время (use_timezone=False) пишется по
местному поясу машины.
- Регулярные выражения — это
re Python: __iposix_regex — re.IGNORECASE, [[:digit:]], \y и
(?n) из PostgreSQL не переводятся, а неверный шаблон даёт исключение с сообщением Python.
- Смена регистра.
Upper(), Lower() и операторы фильтра без учёта регистра — это UPPER(),
LOWER() и LIKE SQLite, которые меняют регистр только у букв ASCII: Upper("café") — это CAFé, а
iexact="ПРИВЕТ" не находит привет — см. Смена регистра.
- Время и адреса.
TimeField и IPAddressField упорядочиваются и сравниваются как хранимый текст:
10:00:00+03:00 идёт после 08:00:00+00:00, хотя это более ранний момент, а 10.0.0.2 — после
10.0.0.10. Чтобы упорядочивать моменты, храните их в DatetimeField.
- Числа. Деление и
MOD на ноль дают NULL, как и математическая функция вне области определения
(Ln(0), Sqrt(-1)); PostgreSQL даёт ошибку. Round() от float — ROUND() SQLite. Greatest() и
Least() дают NULL, когда один из аргументов NULL; PostgreSQL пропускает NULL.
- Текстовые функции.
Substr, Chr и Ord — это substr(), char() и unicode() SQLite. Left,
Right, LPad, RPad, Repeat, Reverse и хэши — функции hare, без 32-битного предела длины,
позиции и количества; результат длиннее того, что SQLite держит в одном значении (1e9 байт),
отклоняется. Текстовая функция, получившая выражение не того типа, оставляется на усмотрение SQLite.
JSONObject() и JSONArray() пишет json_object(): логическое как 1 или 0, float с 15 значащими
цифрами (с SQLite 3.53 — с 17), байты — заглавной шестнадцатеричной записью.
Запросы
| SQLite |
hare |
Строки многострочного RETURNING в порядке вставки |
bulk_create(returning=True) требует pk у каждого объекта и сопоставляет строки по нему — см. bulk_create(). |
DISTINCT ON |
distinct(*fields) бросает UnSupportedError — см. distinct(). |
| Наборы группировок |
Rollup, Cube, GroupingSets и Grouping() бросают UnSupportedError — см. Несколько группировок. |
| Регулярные выражения POSIX |
__posix_regex и __iposix_regex требуют install_regexp_functions=true и сопоставляются re Python с его синтаксисом и семантикой — см. Регулярные выражения. |
| Больше 64 таблиц в одном запросе |
Запрос падает с OperationalError, который говорит об этом; сократите пути по связям. |
Длинные списки __in как параметры |
Начиная с 20 значений список передаётся одним массивом JSON, который читает json_each() — см. Длинные списки __in. |
Схема и миграции
| SQLite |
hare |
ALTER TABLE для большинства изменений колонок и ограничений |
Таблица перестраивается: копия создаётся по модели, заполняется из старой таблицы и переименовывается на её место — см. Перестройка таблицы (SQLite). |
| Проверка внешних ключей во время перестройки |
Каждая миграция идёт с PRAGMA foreign_keys = OFF и проверяет строки через PRAGMA foreign_key_check — см. Внешние ключи во время миграции (SQLite). |
| Схемы |
Meta.schema называет присоединённую базу, которую присоединяет соединение (attached_databases); миграции её не создают — см. Присоединённые базы. Схема на арендатора и построчная защита бросают UnSupportedError — см. Схема на арендатора. |
| Триггеры на команду, отложенные триггеры и ограничения, выключенный триггер |
TriggerForEach.STATEMENT, deferrable=True и enabled=False бросают UnSupportedError — см. Триггеры. |
NULLS NOT DISTINCT |
UniqueConstraint(nulls_distinct=False) бросает ConfigurationError при привязке модели. |
Колонки INCLUDE, место NULL в ключе индекса |
Индекс создаётся без колонок include; ключ, задающий место NULL, бросает UnSupportedError — см. Индексы. |
| Имена, отличающиеся только регистром букв |
Две таблицы одной схемы, две колонки модели, два именованных ограничения или индекса с такими именами ("Note" и "note") дают ConfigurationError при привязке моделей — SQLite считает их одним именем. |
LIKE в условии ограничения или индекса |
Текстовый оператор фильтра записывается как GLOB и находит те же строки с учётом регистра — см. Ограничения. |
| Функции hare вне hare |
Ограничение или индекс, которым нужна такая функция, бросают UnSupportedError — см. В ограничениях и индексах. |
| DDL правил сравнения и расширений |
CreateCollation/RemoveCollation и CreateExtension/RemoveExtension пропускаются с предупреждением — см. Правила сравнения. |
AlterField к другому типу отклоняет только хранимые значения, которые новое поле не прочтёт, до
перестройки (the column holds "abc", which is no integer). Числа и логические значения превращаются
друг в друга (ненулевое число — истина), число, ставшее целым, теряет дробную часть, логическое, ставшее
текстом, — собственный текст SQLite (1 или 0), а текст, ставший логическим, читается как true,
false, 1 или 0.
Поиск и геоданные
| SQLite |
hare |
| Конфигурации текстового поиска |
FullTextIndex поддерживает таблицу FTS5 триггерами; SearchRank и SearchHeadline — это bm25(), highlight() и snippet() FTS5: NULL для строки, которую запрос не находит, а SearchHeadline требует start_sel и stop_sel — см. Полнотекстовый поиск. |
| Векторные типы |
sqlite-vec, с load_sqlite_vec=true — см. Векторный поиск. |
| Пространственные типы |
SpatiaLite, с load_spatialite=true — см. ГИС. |