Парсинг плейсхолдеров strikes back

riff

Новичок
А такое(или нечто подобное) будет или нафиг-нафиг?
Код:
query('
    UPDATE products SET ?ifss -- ассоц. массив с проверкой данных
', [
    [
        'count' =>17,
        'price'=>12.34,
        'title'=>'мама',
        'abcd'=>'мыла'
    ]
]);
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
riff, вообще-то, для твоего варианта с комплексными выражениями, надо забывать, что в этом случае скобки - это не модификатор уже, а
полноправный плейсхолдер. из чего следует, что их надо точно так же типизовать и именовать.
а это уже задачка как-то в лоб не решаемая. Так что я бы все=таки отказался от нее совсем.

И оставил только как модификатор:
Код:
[?i]
[?int]
[s:name]
[str:name]
 

riff

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

То есть, я реально не понимаю, зачем два раздельных плейсхолдера для идентификаторов.
Объяснение простое: я сейчас, без подсказки, не могу объяснить происхождение "n", надо лезть читать его действия.
и второе: "select id, name from pages" -> "выбрать колонки из таблицы" - я могу вспомнить названия плейсхолдеров просто прочитывая sql. Передавая "?c" имя колонки, я знаю, что оно не превратится в строку или т.п..
А, например, "?p" (из темы "пятница. .... Париснг...") я, кажется не пользовался ни разу, потому что, каждый раз, не могу сходу вспомнить его название, а потом пугаюсь не кавычит ли он мне там.
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
В ассоциативных массивах порядок элементов не гарантирован
обоснование, пожалуйста, или уточнение что ты имеешь ввиду;
порядок элементов массива в php имеет значение и сохраняется

PHP:
echo ['a'=>1,'b'=>2] == ['b'=>2,'a'=>1] ? 1:0;
echo ['a'=>1,'b'=>2] === ['b'=>2,'a'=>1] ? 1:0;
echo ['a'=>1,'b'=>2] === ['a'=>1,'b'=>2] ? 1:0;
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
я предлагаю сделать:
1. простой лексер, который выделяет плейсхолдеры из текста по знаку ? или :, можно даже на конечном автомате
2. набор классов с логикой для каждого базового типа данных - модели,
3. компилятор, который для каждого плейсхолдера создает объект с данными нужного типа,
4. контроллер, который читает конфиг, конфигурирует компилятор соответствий плейсхолдеров классам типов, запускает лексер передает модели в отображение
5. набор отображений, которые рендерят запрос для mysql, postgres, для нативных плейсхолдеров, в plain sql и в brainfuck

потом предлагать флаг в задницу и пилить свой синтаксис, алиасы, типы данных и преобразования под свои вкусы

p.s. подумал, что не все прочтут мои мысли, так что:
1. автомат медленный, но структуру после парсинга кешировать очень легко,
2. чтоб добавить свои типы данных можно просто дописать класс с валидацией и приведением к строке - все эти ваши массивы вида ?[n=s]
4. естественно, через конфиг можно подменить лексер или задать свои алиасы,
да, выглядит громоздко, но если не выделять слои, кода меньше не станет :), и писать быстрее, чем мы все это обсуждаем

p.p.s а еще все это обернуть для psr-4, ставить из composer, нарисовать логотип, снять рекламу с няшными девочками, и пропиарить на конфах как новое слово в работе с базой данных и nosql-killer :D
 
Последнее редактирование:

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
я предлагаю сделать:
1. простой лексер, который выделяет плейсхолдеры из текста по знаку ? или :, можно даже на конечном автомате
2. набор классов с логикой для каждого базового типа данных - модели,
3. компилятор, который для каждого плейсхолдера создает объект с данными нужного типа,
4. контроллер, который читает конфиг, конфигурирует компилятор соответствий плейсхолдеров классам типов, запускает лексер передает модели в отображение
5. набор отображений, которые рендерят запрос для mysql, postgres, для нативных плейсхолдеров, в plain sql и в brainfuck

потом предлагать флаг в задницу и пилить свой синтаксис, алиасы, типы данных и преобразования под свои вкусы

p.s. подумал, что не все прочтут мои мысли, так что:
1. автомат медленный, но структуру после парсинга кешировать очень легко,
2. чтоб добавить свои типы данных можно просто дописать класс с валидацией и приведением к строке - все эти ваши массивы вида ?[n=s]
4. естественно, через конфиг можно подменить лексер или задать свои алиасы,
да, выглядит громоздко, но если не выделять слои, кода меньше не станет :), и писать быстрее, чем мы все это обсуждаем

p.p.s а еще все это обернуть для psr-4, ставить из composer, нарисовать логотип, снять рекламу с няшными девочками, и пропиарить на конфах как новое слово в работе с базой данных и nosql-killer :D
Бгыгыгы, я тут практически допилил свой велосипед, с query builder'ом на основе парсера. В нём есть практически всё вышеперечисленное, только вместо своего недоязыка я для указания типов использую стандартные typecast'ы.

PHP:
$connection = new Connection('...');
$factory  = new StatementFactory($connection);

// покажем-ка пять последних новостей
/* @var $query Select */
$query  = $factory->createFromString(<<<QUERY
select n.* from news as n order by news_added desc limit 5
QUERY
);

// ой, нам надо показать ещё и картинки к новости
$query->list[] = 'p.*';
$query->from[0]->join('pictures as p', 'left')->on = 'n.picture_id = p.picture_id';

// и выбрать только новости из нужных разделов
$query->from[] = 'objects_rubrics as ro';
$query->where->and_('ro.rubric_id = any(:rubric::integer[]) and ro.obj_id = n.news_id');

// и не очень старые, к тому же
$query->where->and_('age(news_added) < :age::interval');

// в $generated лежит запрос и информация о маппинге именованные параметры -> позиционные и о типах параметров
// эту хрень можно легко кэшировать, не гоняя потом ни парсер, ни билдер
$generated = $factory->createFromAST($query);

$result = $generated->executeParams($connection, array(
  'rubric' => array(19, 20, 21),
  'age'  => 120 * 24 * 3600
));
Фактически в executeParams() будет дёрнут pg_query_params() с запросом
Код:
select n.*, p.*
from news as n left join pictures as p on n.picture_id = p.picture_id, objects_rubrics as ro
where ro.rubric_id = any($1::pg_catalog.int4[])
  and ro.obj_id = n.news_id
  and age(news_added) < $2::interval
order by news_added desc
limit 5
А параметры попадут в него в виде
Код:
array(2) {
  [0] =>
  string(16) "{"19","20","21"}"
  [1] =>
  string(16) "10368000 seconds"
}
ибо типы мы знаем.
 

riff

Новичок
От идеи автоматом определять массивы я отказался
Я думаю над вариантами
Код:
SELECT id FROM t WHERE cat IN (?ai);
Фанат, подумай над идеей флоппик'а "?ii", "?ss" для обозначения массивов. Всё-таки она, лично мне, опять нравится (даже очень).
 
Последнее редактирование:

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
я предлагаю сделать:
1. простой лексер, который выделяет плейсхолдеры из текста по знаку ? или :, можно даже на конечном автомате
Ну и кстати да, хочу отметить, что без нормального лексера оно в любом случае решаться не будет. Причём своего на каждый диалект, как иначе обрабатывать те же строки в долларах из Postgres'а: $foo$Placeholders look like this: ?ai or this [i:bar]$foo$

И синтаксис плейсхолдеров хорошо бы продумать так, чтобы он по минимуму пересекался с конструкциями всех возможных диалектов SQL. Так, например, в Postgres'е квадратные скобки используются для элементов массивов: foo[1], двоеточие может использоваться для получения куска массива: foo[1:3], а вопросительный знак может быть именем оператора (или его частью), как, например, в hstore: foo ? bar. Вот фигурные скобки вроде нигде не используются?

Даже у меня получается проблема из-за плейсхолдеров вида :foo, ибо в штатном Postgres'е конструкция foo[bar:baz] будет обработана как "часть массива foo от bar до baz", а у меня будет parse error, потому что :baz разберётся как имя плейсхолдера. Т.е. обязательно фигачить пробелы вокруг ':'.
 

Фанат

oncle terrible
Команда форума
Даже у меня получается проблема из-за плейсхолдеров вида :foo, ибо в штатном Postgres'е конструкция foo[bar:baz] будет обработана как "часть массива foo от bar до baz", а у меня будет parse error, потому что :baz разберётся как имя плейсхолдера. Т.е. обязательно фигачить пробелы вокруг ':'.
Я все хочу собраться и написать максимально простой рег, который будет пропускать плейсхолдеры внутри кавычек и бэктиков. В принципе, добавить к ним еще пару квадратных скобок, может решить проблему...
 

AmdY

Пью пиво
Команда форума
Фанат, а почему не сделать наоборот, плейсхолдеры оставить как есть, а для спецсимволов добавить экранирование.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Я все хочу собраться и написать максимально простой рег, который будет пропускать плейсхолдеры внутри кавычек и бэктиков. В принципе, добавить к ним еще пару квадратных скобок, может решить проблему...
Чё-то решение так себе, почему я не могу, например, выбирать нужный мне элемент массива, используя подстановки: select foo[:bar] from arraysource?
 

Фанат

oncle terrible
Команда форума
Фанат, а почему не сделать наоборот, плейсхолдеры оставить как есть, а для спецсимволов добавить экранирование.
Так я и хочу оставить плейсхолдеры, как есть.
Но экранирование мне кажется лишним и ненужным. Хотя, возможно, в каких-то случаях оно не будет.

Я просто хочу, чтобы в запросе
Код:
SELECT `:name` FROM t WHERE f = 'foo?sar'
Парсер не находил плейсхолдеров (сейчас находит).
Я понимаю, что примеры высосаны из пальца и в реальной жизни практически не встречаются.
Но я к тому, что если добавить пару [] к парам '' и `` для пропуска - то и экранировать не нужно будет.
 

Фанат

oncle terrible
Команда форума
Чё-то решение так себе, почему я не могу, например, выбирать нужный мне элемент массива, используя подстановки: select foo[:bar] from arraysource?
Ну, ты прав, конечно.
А что ты думаешь насчет экранирования? или текущий вариант с пробелами тебя устраивает?
 

Фанат

oncle terrible
Команда форума
Вообще, все эти ужасы синтаксиса различных СУБД все больше укрепляют меня в идее писать только под мускуль. Задача, по крайней мере, видится мне подъёмной :)
 
  • Like
Реакции: AmdY

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Ну, ты прав, конечно.
А что ты думаешь насчет экранирования? или текущий вариант с пробелами тебя устраивает?
А что предполагается экранировать? Символ ':'? По-моему пробелы проще и очевидней.

Вообще рекомендую глянуть, как в том же PEAR::MDB2 и Doctrine::DBAL (который кое-где является логическим продолжением) сделан парсинг плейсхолдеров. Подсказка: одним регексом они там не обходятся.

Вообще, все эти ужасы синтаксиса различных СУБД все больше укрепляют меня в идее писать только под мускуль. Задача, по крайней мере, видится мне подъёмной :)
Ну это да, моя вышепроцитированная хрень тоже работать может исключительно с диалектом Postgres'а, полагаясь на кучу специфических его фич.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
а я скучаю по постгресу, за 2 года в ecommerce я почти каждый день, сталкиваясь с DBA-задачами, думал "зачем?! эти костыли, эта ущербность, эта невнятная симуляция SQL!"
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Вообще, все эти ужасы синтаксиса различных СУБД все больше укрепляют меня в идее писать только под мускуль. Задача, по крайней мере, видится мне подъёмной :)
не вижу проблемы: пишем лексер под mysql, если кто-нибудь напишет под другие базы - хорошо,

а идея насчет smarty-style {$param|int} - можно и подумать, но

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Sad Spirit, а твоя либа - она встраиваемая, в принципе, во фреймворк?
Уж очень круто выглядит.
 
Сверху