Возьмите чистую кастрюлю...
На самом деле тут потребуется шаг, на котором споткнется процентов 90 всех проектов:
мы не сможем ничего сгенерировать и проанализировать, если запросы находятся вперемешку с кодом приложения.
Нужно вынести SQL в отдельные файлы.
Плюсов у этого приблизительно дофига, а минус ровно один - это нужно сделать :)
Во-первых, SQL-код становится более качественным, когда у вас пропадает возможность клеить его в рантайме.
Во-вторых, увеличивается покрытие тестами.
В-третьих, теперь изменения запросов в системе контроля версий можно отслеживать отдельно от кода приложения.
Так же улучшается и архитектура самого приложения: там, где раньше была лапша if'ов и склейки строк, теперь пяток отдельных методов.
И для каждого описаны типы!
Ну допустим. А дальше?
А дальше берем любой шаблонизатор...
Нет, ну можно, конечно, заморочиться с каким-нибудь конструктором AST и депарсером, но вы помните, что мы все еще делаем MVP?
Короче - у нас есть типы и теперь мы можем превратить их в ваши модели, POJO или json-schema
Заметьте, что мы уже реализовали 2/3 плюсов ORM, о которых говорили вчера. Теперь нам не надо думать о соответствии типов в приложении и базе и у нас есть кодогенерация!
Но все-таки из списка проблем на границе базы и приложения решена только 1 проблема из 5.
В моем случае такая тулза сэкономила где-то процентов 30 рабочего времени. Если бы не это извращение с регекспами, сэкономило бы все 50.
В принципе, тут бы можно было и остановиться, ситуация уже лучше, чем была до начала разговора.
Но мы ж поргомисты, пляшем дальше...
А вот дальше нам нужно сделать качественный скачок от как-то полученных типов к анализу запросов.
И тут вариант один - нам нужен AST парсер.
Если у нас будет AST мы сможем сделать как минимум linter-like правила.
Например, второе правило вторника - avoid typecast IS [NOT] NULL
И это моя особая боль. Официальных AST парсеров для диалектов SQL можно сказать, что нет.
Ныть об этом я могу еще один день, но это вряд ли кому-то будет интересно.
Если коротко - вам придется много программировать. Ну или не придется, если вы не бросите читать меня до завтра :)
Глобально нам повезло. Для #PostgreSQL есть AST парсер, который смогли выковырять из сервера:
https://github.com/lfittl/libpg_query
Есть обертки для разных языков.
Минус один - стабильный парсер есть только диалекта десятой версии.
Если к информации о типах и AST мы добавим данные из INFORMATION_SCHEMA (да, придется опять использовать живую базу), то мы при должном упорстве даже сможем понимать типы в подзапросах.
Это все даст нам возможность придумывать более сложные правила для анализатора.
И даже в некоторых простых случаях мы сможем следить за связанностью полей.
Как можно догадаться, linter-like правила на AST это тот рубеж, после которого начинается настоящее месилово.
Потом нам понадобится описание всех типов всех функций, операторов, правила выполнения implicit typecast и много-много всякого...
Например, только из AST нельзя однозначно понять что обозначает * в запросе
SELECT t.* FROM tЗдесь t.* это не table = 't', columns = '*', а массив ['t', '*']
Вы можете сказать - "ну вон же, FROM t"!
А если есть колонка t.t? А если она композитного типа?
Короче, все сложно...
Дальше либо стоит остановиться (мы и так неплохо поработали), либо придется писать КОМПИЛЯТОР SQL в Intermediate Representation Object, фактически повторяя поведение базы...
Я, пожалуй, остановлюсь, т.к. компилятор это явно перебор :)
Скажу лишь, что я его написал*...
Поэтому завтра будут истории о том, как просто можно им воспользоваться уже сейчас и какие бенефиты вы от этого получите!