Сообщение Re[7]: [proof-of-concept] compile time query language от 14.07.2016 7:51
Изменено 14.07.2016 7:53 chaotic-kotik
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.
А что если у меня условия формируются динамически? Допустим есть сервис, к сервису происходит обращение вида GET http://path?field1<100&field2>200 (не нужно делать такие url в реальных проектах) а задача сервиса — понять как сформировать запрос и преобразовать его в какой-нибудь JSON например. Ну или limit/offset clauses, тоже всегда динамически выставляются. В общем, запрос в compile time это очень примитивный сценарий использования. В таких случаях список запросов уже совсем не просто получить, придется использовать средства профилирования БД. Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.
EP>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.
Ну это я для простоты через * написал, само собой так делать не нужно никогда.
CK>>
EP>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.
Ну по сути да. Можно это в expression template запихать, но тогда теряется динамика. Еще из такого можно легко prepared statement сгенирировать, а в статике нельзя (ну либо твой EDSL начнет работать полностью в динамике).
EP>Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.
Ну так если сделать так, как я выше описал то не придется. Я вдел реализации, которые позволяют запросы генерировать из AST, который можно например сериализовать в промежуточный формат или например получить AST из строчки URL или POST-данных HTTP запроса а потом сделать из этого запрос. Так же этот AST можно было анализировать перед исполнением на предмет ошибок и SQL injection. При этом AST был (само собой) абстрактным и не содержал названий полей и таблиц, вместо этого он оперировал сущностями предметной области.
EP>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.
Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд. Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо. И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать. Построение запроса это слезы, этот запрос потом отправляется на другую машину, где он парсится, оптимизируется и исполняется. За время оптимизации запроса можно его миллион раз сгенерировать повторно. Zero overhead преобразование результатов тоже мало полезного дает — некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.
EP>SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.
А что если у меня условия формируются динамически? Допустим есть сервис, к сервису происходит обращение вида GET http://path?field1<100&field2>200 (не нужно делать такие url в реальных проектах) а задача сервиса — понять как сформировать запрос и преобразовать его в какой-нибудь JSON например. Ну или limit/offset clauses, тоже всегда динамически выставляются. В общем, запрос в compile time это очень примитивный сценарий использования. В таких случаях список запросов уже совсем не просто получить, придется использовать средства профилирования БД. Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.
EP>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.
Ну это я для простоты через * написал, само собой так делать не нужно никогда.
CK>>
CK>>Filter flt;
CK>>flt.include_if(Placement.Fields.RECT, Filter.GT, BoundingRectangle(100, 200));
CK>>auto placements_list = publisher.get_placements(filter); // executes `SELECT * FROM "Placements" WHERE id = 42 and (width > 200 and height > 100)`
CK>>
EP>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.
Ну по сути да. Можно это в expression template запихать, но тогда теряется динамика. Еще из такого можно легко prepared statement сгенирировать, а в статике нельзя (ну либо твой EDSL начнет работать полностью в динамике).
EP>Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.
Ну так если сделать так, как я выше описал то не придется. Я вдел реализации, которые позволяют запросы генерировать из AST, который можно например сериализовать в промежуточный формат или например получить AST из строчки URL или POST-данных HTTP запроса а потом сделать из этого запрос. Так же этот AST можно было анализировать перед исполнением на предмет ошибок и SQL injection. При этом AST был (само собой) абстрактным и не содержал названий полей и таблиц, вместо этого он оперировал сущностями предметной области.
EP>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.
Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд. Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо. И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать. Построение запроса это слезы, этот запрос потом отправляется на другую машину, где он парсится, оптимизируется и исполняется. За время оптимизации запроса можно его миллион раз сгенерировать повторно. Zero overhead преобразование результатов тоже мало полезного дает — некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.
Re[7]: [proof-of-concept] compile time query language
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.
А что если у меня условия формируются динамически? Допустим есть сервис, к сервису происходит обращение вида GET http://path?field1<100&field2>200 а задача сервиса — понять как сформировать запрос и преобразовать его в какой-нибудь JSON например. Ну или limit/offset clauses, тоже всегда динамически выставляются. В общем, запрос в compile time это очень примитивный сценарий использования. В таких случаях список запросов уже совсем не просто получить, придется использовать средства профилирования БД. Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.
EP>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.
Ну это я для простоты через * написал, само собой так делать не нужно никогда.
CK>>
EP>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.
Ну по сути да. Можно это в expression template запихать, но тогда теряется динамика. Еще из такого можно легко prepared statement сгенирировать, а в статике нельзя (ну либо твой EDSL начнет работать полностью в динамике).
EP>Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.
Ну так если сделать так, как я выше описал то не придется. Я вдел реализации, которые позволяют запросы генерировать из AST, который можно например сериализовать в промежуточный формат или например получить AST из строчки URL или POST-данных HTTP запроса а потом сделать из этого запрос. Так же этот AST можно было анализировать перед исполнением на предмет ошибок и SQL injection. При этом AST был (само собой) абстрактным и не содержал названий полей и таблиц, вместо этого он оперировал сущностями предметной области.
EP>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.
Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд. Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо. И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать. Построение запроса это слезы, этот запрос потом отправляется на другую машину, где он парсится, оптимизируется и исполняется. За время оптимизации запроса можно его миллион раз сгенерировать повторно. Zero overhead преобразование результатов тоже мало полезного дает — некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.
EP>SQL код генерируется во время компиляции — легко получить список всех запросов. Либо из asm/бинарного кода, либо просто убрать определение значения строки — тогда компилятор выведет список всех запросов в ошибках компиляции/линковки.
А что если у меня условия формируются динамически? Допустим есть сервис, к сервису происходит обращение вида GET http://path?field1<100&field2>200 а задача сервиса — понять как сформировать запрос и преобразовать его в какой-нибудь JSON например. Ну или limit/offset clauses, тоже всегда динамически выставляются. В общем, запрос в compile time это очень примитивный сценарий использования. В таких случаях список запросов уже совсем не просто получить, придется использовать средства профилирования БД. Ну и весь type safety куда-то внезапно улетучивается и становится окончательно непонятно, с чего это вдруг мы используем какой-то EDSL.
EP>Так в том и дело, "SELECT *" выдаст все колонки, что часто неэффективно, и в разных местах требуются разные проекции. В случае с DAL придётся либо плодить функции на каждый чих, либо вводить аналогичный EDSL.
Ну это я для простоты через * написал, само собой так делать не нужно никогда.
CK>>
CK>>Filter flt;
CK>>flt.include_if(Placement.Fields.RECT, Filter.GT, BoundingRectangle(100, 200));
CK>>auto placements_list = publisher.get_placements(filter); // executes `SELECT * FROM "Placements" WHERE id = 42 and (width > 200 and height > 100)`
CK>>
EP>А здесь ты точно также создаёшь фильтр на стороне пользователя, только в другой форме, но суть та же.
Ну по сути да. Можно это в expression template запихать, но тогда теряется динамика. Еще из такого можно легко prepared statement сгенирировать, а в статике нельзя (ну либо твой EDSL начнет работать полностью в динамике).
EP>Вот только его абстракция будет постоянно протекать — придётся на каждую проекцию делать функции, точно также реализовывать внешние фильтры, при этом не особо ясно что получаем из бенефитов.
Ну так если сделать так, как я выше описал то не придется. Я вдел реализации, которые позволяют запросы генерировать из AST, который можно например сериализовать в промежуточный формат или например получить AST из строчки URL или POST-данных HTTP запроса а потом сделать из этого запрос. Так же этот AST можно было анализировать перед исполнением на предмет ошибок и SQL injection. При этом AST был (само собой) абстрактным и не содержал названий полей и таблиц, вместо этого он оперировал сущностями предметной области.
EP>EDSL запросов по сути не особо отличается от того же Boost.Range например — практически такое же декларативное описание "запроса" к контейнерам. При этом вызовы Boost.Range ни в какой DAL не прячут.
Ну так boost.range оперирует объектами в коде программы, SQL запрос оперирует данными в таблицах и по хорошему код не должен ничего знать про имена таблиц/полей, layout, join-s и тд. Для этого и нужен слой DAL. Иначе миграции схемы БД превратятся в сложные рефакторинги а это не есть хорошо. И ты ошибочно предполагаешь что построение запроса и конвертация результатов запроса — это то что нужно оптимизировать. Построение запроса это слезы, этот запрос потом отправляется на другую машину, где он парсится, оптимизируется и исполняется. За время оптимизации запроса можно его миллион раз сгенерировать повторно. Zero overhead преобразование результатов тоже мало полезного дает — некоторые БД отдают результаты в виде текста, некоторые предоставляют апи для доступа к данным результатов запросов вида `int getFieldAsInt(int field_index, int default_value)` и что ты вызовешь этот метод в своем EDSL, что класс в DAL вызовет его, разницы никакой.