Возвращаясь к теме Query Builder'ов

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
У меня наконец дошли руки выложить на GitHub свои поделия, которые я тут на форуме неоднократно уже упоминал. В качестве компенсации за долгое ожидание --- они готовы к выходу PostgreSQL 9.4, всё свеженькое поддерживают.

Разбил для удобства на два проекта: https://github.com/sad-spirit/pg-wrapper и https://github.com/sad-spirit/pg-builder Компостер, travis, все дела.

Первый --- обёртка над похапэшным расширением pgsql, реализующая поддержку сложных типов. Ну а так выглядит как стандартный DBAL, хотя таковым, безусловно, не является.

Второй --- это как раз query builder с parser'ом внутрях. Позволяет реализовать любимый Фанатом подход с типизированными placeholder'ами, не вводя новый синтаксис, например.

Жду гнилых помидоров и прочей конструктивной критики!
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
ага, прикольно что DateInterval расширяет нативный,

не нашел пример составления запроса, есть?
 
Последнее редактирование:

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
не нашел пример составления запроса, есть?
Ну там есть прямо в readme pg-builder'а пример со сбором запроса на основе готового.

А для варианта "с нуля" в том же StatementFactory есть методы select() / insert() / update() / delete() / values():
PHP:
$select = $factory->select('n.*', 'news as n');
можно передавать не строки, а куски AST, но это будет изрядно многословно:
PHP:
$relation = new RelationReference(new QualifiedName(array('news')));
$relation->setAlias(new Identifier('n'));
$select = $factory->select(
    new TargetList(array(new TargetElement(new ColumnReference(array('n', '*'))))),
    new FromList(array($relation))
);
 

hell0w0rd

Продвинутый новичок
Почему неймспейсы не в CamelCase?:(
И еще, зачем писать свой парсер, когда можно взять порт bison и свистнуть грамматику напрямую с сорцов постгреса?
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Почему неймспейсы не в CamelCase?:(
Мне так больше нравится, легче визуально понять, где пространство имён, а где класс. На PSR-2 ссылаться не надо, там речь исключительно про имена классов. :)

И еще, зачем писать свой парсер, когда можно взять порт bison и свистнуть грамматику напрямую с сорцов постгреса?
Я, честно говоря, не видел этот самый порт bison'а, когда к работе приступал. А на нём что-нибудь сделано кроме примера-калькулятора, не в курсах?

К тому же тут ещё один тонкий момент: у меня парсер разбирает не только запрос целиком, но и его куски, "с середины" работает. Все эти бизоны так умеют, а то у меня было ощущение, что нет?
 

hell0w0rd

Продвинутый новичок
Sad Spirit, ну это смотря как ты грамматику опишешь, я так понимаю что в любом парсере есть top-statement, в нем можно указать что угодно
https://github.com/postgres/postgres/blob/master/src/backend/parser/gram.y#L731 - вот с чего разбор в постгресе как раз идет, можно туда и куски добавить.

А вообще - зачем с середины-то разбирать? У тебя query-builder строку же генерит, или сразу AST?
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
А вообще - зачем с середины-то разбирать? У тебя query-builder строку же генерит, или сразу AST?
С середины разбирать затем, чтобы работали конструкции типа
PHP:
$query->where->and_('ro.rubric_id = any(:rubric::integer[]) and ro.obj_id = n.news_id');
Здесь мне надо разобрать a_expr из грамматики, а не stmt: https://github.com/postgres/postgres/blob/master/src/backend/parser/gram.y#L11016

У меня query builder работает с AST, потом по нему уже генерируется запрос.

А вообще флоппик прав, у меня стойкое ощущение deja vu.

Судя по отсутствию ответа на вопрос "есть ли что-то реальное, сделанное с использованием порта бизона", такового реального нету?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Ну там есть прямо в readme pg-builder'а пример со сбором запроса на основе готового.

А для варианта "с нуля" в том же StatementFactory есть методы select() / insert() / update() / delete() / values():
$select = $factory->select('n.*', 'news as n');
это понятно, я про параметризацию, в запрос INSERT INTO ( :age ) можно передать
PHP:
':age' => new DateInterval('1 day');
?
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
Sad Spirit, нубский вопрос, а под мускуль оно заработает/будет?

PS: Код просмотрел мельком, если чо не так - снесите мессагу.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
это понятно, я про параметризацию, в запрос INSERT INTO ( :age ) можно передать
PHP:
':age' => new DateInterval('1 day');
?
Да, есессно можно.

Sad Spirit, нубский вопрос, а под мускуль оно заработает/будет?
Из коробки --- нет. Парсер и лексер там специфически постгресовые, но если ограничиваться ANSI SQL, то мысклёвый диалект тоже разберут, наверное. Проблема в том, что в дерево местами пишутся не стандартные конструкции, а специфические, и SqlBuilderWalker придётся основательно переделать, чтобы он выдавал понятный MySQL код.
 

fixxxer

К.О.
Партнер клуба
Вообще сделать базовый ANSI SQL-лексер/парсер и расширять его диалектами - хорошая идея.

Пока в код серьезно не вчитывался, но после беглого взгляда мне видится, что это сделать несложно.

UPD: посмотрел SqlBuilderWalker - вот тут самое, видимо, сложное - я бы вынес конечные автоматы из свитчей в конфигурацию FSM.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Вообще сделать базовый ANSI SQL-лексер/парсер и расширять его диалектами - хорошая идея.
Ну специально для этого на гитхабчике и сделали кнопочку fork.

UPD: посмотрел SqlBuilderWalker - вот тут самое, видимо, сложное - я бы вынес конечные автоматы из свитчей в конфигурацию FSM.
Ммм... Немаленькие свитчи там только в одном месте, вроде бы: в определении приоритета выражений. Можно вообще от них избавиться и везде задавать приоритет скобками, но результирующий запрос будет неэстетичен.
 

Вурдалак

Продвинутый новичок
Если парсить запрос на таком уровне, то достаточно легко организовать правильный cache layer. Сейчас на подобном уровне можно только в СУБД кеш включать, либо отказаться от native queries.
 

fixxxer

К.О.
Партнер клуба
Ну специально для этого на гитхабчике и сделали кнопочку fork.


Ммм... Немаленькие свитчи там только в одном месте, вроде бы: в определении приоритета выражений. Можно вообще от них избавиться и везде задавать приоритет скобками, но результирующий запрос будет неэстетичен.
Про кнопочку знаю =)

Про свитчи, надо бы подумать, как декомпозировать walker, дабы было проще добавлять диалекты.

Если парсить запрос на таком уровне, то достаточно легко организовать правильный cache layer. Сейчас на подобном уровне можно только в СУБД кеш включать, либо отказаться от native queries.
Мне это кажется более уместным на уровне DataMapper, который тут, кстати, вполне строится поверх билдера, примерно как в SqlAlchemy.
 

Вурдалак

Продвинутый новичок
Мне это кажется более уместным на уровне DataMapper, который тут, кстати, вполне строится поверх билдера, примерно как в SqlAlchemy.
Не знаю как в SqlAlchemy. Ты хочешь кешировать сами объекты?

Мне просто кажется это более безопасным на более низком уровне. Ну, типа, где-то есть консольный говноскрипт, который напрямую очищает какие-то таблицы или мы где-то делаем выборку-проекцию на основе нескольких таблиц: здесь придётся вручную указывать список затрагиваемых сущностей.

UPD Хотя, по идее, одно другому не мешает: если библиотека будет просто рейзить события типа TableWasUpdatedEvent, то можно подписываться на них на более высоком уровне и инвалидировать объекты.
 

fixxxer

К.О.
Партнер клуба
Да, одно другому не мешает, просто на уровне em удобнее и уместнее вводить теги с зависимостями для инвалидации по цепочке и подобное.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Поднимем тему, пожалуй. Выкатил новые версии + наконец написал документацию.

* pg_builder'ом поддерживается синтаксис вплоть до свеженького Postgres 10
* выкинут самопальный кэш, используйте любой с поддержкой PSR-6
* Добавлен базовый класс обхода по дереву, реализовать свой на его основе будет несколько проще
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Обход по дереву - задача, про которую много пишут, но в реальности мне за 15 лет она встречалась раз или два. Даже в ecommerce с каталогом в виде дерева работать не приходится - или выводишь все категории списком, или работаешь с одной.
 

fixxxer

К.О.
Партнер клуба
@Sad Spirit, прошло несколько лет, расскажи об опыте использования и юзкейсах на практике.

Я к тому, что штука клевая и проделана огромная работа, но я вот почти не вижу кейсов, когда классического write only билдера было бы недостаточно. Ну, то есть, я могу их специально придумать, но на практике не припоминаю.
 
Сверху