Создание диалекта: гарантии, тестирование и наблюдаемость¶
Шаги 11–13 создания диалекта: что hare делает за базу с меньшими гарантиями, тестирование диалекта и наблюдение за ним.
11. Что hare делает для базы без гарантий¶
| У базы нет | hare |
|---|---|
| Транзакций | Отклоняет atomic(); свои записи из нескольких команд выполняет по одной команде. |
| Точек сохранения | Присоединяет вложенный atomic() к транзакции, которая откатывается целиком, если вложенный блок завершился ошибкой. |
| Генерируемых ключей | Отклоняет при привязке модель, чей первичный ключ генерирует база, — или берёт её ключи из серии до вставки (takes_keys_before_insert). |
UPDATE / DELETE сохранённых строк |
Отклоняет каждую такую команду до отправки; весь каскад проверяется до первой записи, поэтому база без транзакций никогда не остаётся с половиной каскада. |
| Связанных подзапросов | Пишет EXISTS, связанный равенствами колонок, как проверку принадлежности; любой другой связанный подзапрос отклоняет до отправки. |
| Внешних ключей | Выполняет on_delete и PROTECT в Python; отклоняет связь с db_constraint=True при привязке её модели. |
| Ограничений уникальности | Отклоняет модель, объявившую уникальность, при привязке, а миграцию, объявляющую её, — до её первой команды. |
ON CONFLICT |
Отклоняет параметры конфликтов bulk_create() до отправки SQL (supports_insert_conflicts). |
RETURNING |
UPDATE не читает изменённые вычисляемые колонки; значения, которые база даёт записанной строке, остаются в объекте непрочитанными (их читает refresh_from_db()); returning() отклоняется. |
| Числа записанных строк | update(), delete() и bulk_update() возвращают None; модель с Meta.optimistic_lock_field отклоняется при привязке (reports_written_row_count). |
| Блокировок строк | Отклоняет select_for_update(). |
| Отложенных ограничений | TableClearing.defer_cascade_foreign_keys() даёт UnSupportedError — настоящее удаление через защиту PROTECT в том же каскаде нельзя отложить. |
| Первичного ключа | Поддерживает модели без ключа, как описано выше. |
Features.checks_foreign_keys_per_cascade_step, checks_restrict_at_statement_end и
cascade_depth_limit описывают, как ведёт себя собственный каскад базы, чтобы hare знал, когда
откладывать его или выполнять самому. Клиент, у которого в возможностях задан cascade_depth_limit, даёт
CascadeDepthLimitError из hare.exceptions — или свой подкласс, как SqliteTriggerRecursionLimitError
у SQLite, — когда база останавливает каскад на этой глубине; delete() перехватывает её, откатывает
попытку и проходит каскад в Python.
12. Тестирование¶
Собственный набор тестов hare работает на диалекте: установите пакет (его точка входа регистрирует
драйвер) и направьте на него HARE_TEST_DB (colstore+colstore-client://.../test_{}, место {}
заполняется при каждом запуске) — выполнится каждый тест, который допускают возможности диалекта. Тест
того, чего у базы может не быть, помечается нужными ему возможностями:
from hare.contrib.test import requires_features
@requires_features(supports_transactions=True)
async def test_rollback(db): ...
@requires_features(supports_foreign_keys=True)
async def test_cascade_in_the_database(db): ...
tests/dialects/test_dialect_contract.py проверяет каждый зарегистрированный диалект и драйвер: имена, схемы,
возможности, то, что каждая часть построена для своего диалекта, реестры, редактор схемы, чтение схемы и
то, что SQL строится одинаково при повторе. Между тестами база очищается через
TableClearing.clear_tables().
13. Наблюдение и скорость¶
otel_system_name — значение db.system.name в OpenTelemetry (postgresql, clickhouse или
other_sql для базы, которой OpenTelemetry имени не даёт). Чтение строк на Rust зависит от
native_python_types клиента, а не от имени диалекта. Каждая команда, которую выполняет клиент, доходит
до Observers — QueryExecuted, обёртки запросов, журнал
медленных запросов — через декоратор, описанный в разделе о клиенте;
клиент, который его пропускает, невидим для capture_queries, поиска N+1 и OpenTelemetry.