Напиши на Python 3.12 мини-движок SQL: один модуль, только стандартная библиотека. Модуль `sqlite3` и любые внешние СУБД/библиотеки использовать НЕЛЬЗЯ. eval/exec/compile тоже нельзя. ## Интерфейс ```python def run_query(tables: dict[str, tuple[list[str], list[tuple]]], sql: str) -> list[tuple]: ... ``` - `tables`: имя таблицы → (список имён колонок, список строк). Значения в ячейках: `int`, `float`, `str` или `None`. - Возвращает строки результата — список кортежей. - Ошибочный SQL, несуществующая таблица/колонка/функция, неверное число аргументов функции → `ValueError`. ## Главное требование Результат (значения, ТИПЫ значений int/float/str/None, число строк, порядок при ORDER BY) должен совпадать с тем, что вернул бы **SQLite 3.45**, если создать таблицы без объявленных типов колонок (`CREATE TABLE t(a, b, c)`, т.е. без type affinity), вставить те же значения и выполнить тот же запрос. SQLite собран без ICU. Всё, что явно не описано ниже, — как в SQLite. ## Поддерживаемый SQL Ключевые слова, имена функций, таблиц и колонок — регистронезависимы. В конце запроса может стоять `;`. ``` SELECT [DISTINCT] <список> FROM <таблица> [[AS] <алиас>] { [INNER] JOIN <таблица> [[AS] <алиас>] ON <выражение> | LEFT [OUTER] JOIN <таблица> [[AS] <алиас>] ON <выражение> | CROSS JOIN <таблица> [[AS] <алиас>] }* [WHERE <выражение>] [GROUP BY <выражение>, ...] [HAVING <выражение>] [ORDER BY <выражение> [ASC|DESC], ...] [LIMIT <целое> [OFFSET <целое>]] ``` - `<список>`: `*`, `<алиас>.*`, выражения с необязательным `[AS] <имя>`. - Колонки: `col` или `t.col` (t — имя таблицы или её алиас). - GROUP BY и ORDER BY могут ссылаться на алиас из списка SELECT; ORDER BY — ещё и на номер колонки результата (с 1). Выражения: - литералы: целые (`42`), вещественные (`1.5`, `.5`, `2.`), строки `'it''s'`, `NULL`; - унарные `-`, `+`, `NOT`; - бинарные: `||`, `*`, `/`, `%`, `+`, `-`, `<`, `<=`, `>`, `>=`, `=`, `==`, `!=`, `<>`, `IS`, `IS NOT`, `AND`, `OR` — с приоритетами SQLite; - `x IS NULL`, `x IS NOT NULL`, `x [NOT] BETWEEN a AND b`, `x [NOT] IN (a, b, ...)`, `x [NOT] LIKE pattern`; - `CASE WHEN c THEN v ... [ELSE v] END` и `CASE x WHEN v THEN r ... [ELSE r] END`; - скобки; - скалярные функции: `ABS`, `LOWER`, `UPPER`, `LENGTH`, `COALESCE` (2+ аргумента), `IFNULL`, `NULLIF`, `ROUND(x)`, `ROUND(x, n)`, `SUBSTR(s, start)`, `SUBSTR(s, start, len)`; - агрегаты: `COUNT(*)`, `COUNT(x)`, `COUNT(DISTINCT x)`, `SUM`, `AVG`, `MIN`, `MAX`. Агрегаты могут входить в выражения (`SUM(a) * 2 + 1`, `CASE WHEN COUNT(*) > 1 THEN ...`), встречаться в HAVING и ORDER BY, в том числе такие, которых нет в списке SELECT. Запрос с агрегатами без GROUP BY возвращает ровно одну строку — даже по пустой таблице. ## Что НЕ будет проверяться Подзапросы; неоднозначные неквалифицированные колонки; «голые» колонки вне GROUP BY в агрегатном запросе; HAVING без GROUP BY; арифметика над строками; сравнение числа со строкой; `%` с вещественными операндами; переполнение int64; порядок строк без ORDER BY; порядок строк с равными ключами ORDER BY. ## Производительность Таблицы до 50 000 строк. JOIN двух таблиц по 50 000 строк по условию равенства колонок (возможно, с дополнительными условиями через AND), затем WHERE/GROUP BY/ORDER BY — не дольше 3 секунд на обычном ПК. ## Формат ответа Ответь ТОЛЬКО кодом модуля в одном блоке ```python.