Перейти к содержанию

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 — см. ГИС.