! | Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам) |
Синтаксис Qx предназначен для безопасного и одновременно юзабильного составления портабельных SQL запросов из C++. Cx транслирует Qx выражения в низкоуровневый С++ код, составляющий текст SQL-запроса и осуществляющий биндинг локальных переменных С++ c запросом. Все это typesafe с максимальным статическим контролем.
Важно понимать что Qx это не тупиковый хак со "встроенным в С++" SQL-подобным языком. Udb формирует все запросы динамически, позволяя приложению варьировать любые элементы SQL-выражений но при этом статически, на этапе компиляции отслеживая корректность итогового SQL, совместимость типов данных и оптимизируя простые частные случаи.
Qx не является коммерческим интерфейсом для которого приоритетно произвести феерическое первое впечатление на наивного покупателя кажущейся простотой и интегрированностью SQL. Qx имеет универсальный синтаксис приоритет которого - масштабируемость на самые сложные случаи а не на простые демонстрационные примерчики. Единый синтаксис Qx не требует в сложных (реальных) приложениях переключатся на опасные манипуляции SQL в текстовых строчках, медленные и нечитабельные билдеры выражений или немаинтенабельное мракобесие с метаинформацией и путешествиям по парсерным деревьям.
Простейший пример запроса - отфильтровать из таблицы Users всех пользователей с незаданным email:
rQuery->x_qxboolWhere = Qx fromUser->x_sUserEmail == "";
Просто, понятно, парсится текстовым редактором подсказывающим проперти для С++ темплейта from<>, типы данных контролируются при сравнении в выражении и при присваивании x_qxboolWhere имеем логический тип.
Теперь сложный пример когда выражения запроса добавляются условно, и собираются из кусочков, гибко инкапсулировано и интегрируются вместе без потери читабельности, с тем же надежным контролем корректности:
// a part of query depends on an option if (bShowOnlySelected) { qxboolFilterUser = Qx fromUser->x_bSelected; } // somewhere else there is a time-interval-selector qxbool qxboolDateFilter = GetQxDateFilter(); // finalize composite query rQuery->x_qxboolWhere = Qx (qxboolFilterUser) && (qxboolDateFilter) && !fromVisit->x_bCloakedAdmin; // variable ORDER BY depends on UI settings qxscalar qxscalarOrderBy = GetCurrentOrder(); rQuery->x_qxsortOrderBy = Qx (qxscalarOrderBy) ORDER(eCurrentOrder), fromVisit->x_utcStarted ORDER(E_Order_Descending);
Нам не пришлось прибегать ни к слеплению SQL текстом, ни к насыщению условия запроса хаками, выбирающими его опциональные элементы. SQL код запроса минимален, четко отделен от C++ кода, его формирующего.
Udb предназначена для обслуживания приложений которые не копят в памяти иерархии данных в объектах а хранят все в реляционной форме. И в Qx не планируется поддерживать хаки с псевдо-реляционными запросами к нереляционным данным. Qx формирует SQL для передачи реляционной СУБД и не занимается ORM для иерархий объектов. (Эта установка не исключает естественно необходимых механизмов кеширования в памяти осуществляемого на разных уровнях инкапсуляции хранилища и подстановки данных из нереляционных источников на уровне драйверов СУБД или прикладных хранилищ.)
Не надо воспринимать Qx как магическую абстракцию скрывающую SQL и все особенности, проблемы и преимущества реляционной СУБД. SQL надо знать, реляционную структуру надопроектировать. Цель применения Qx - это составление стандартного SQL запроса, который на низком уровне будет представлен в текстовой форме для последующей интерпретации бакендом и удобный биндинг С++ переменных на каждой итерации запроса.
Нужно четко различать
Qx не допускает произвольного смешения операторов и функций C++ c SQL кодом. Синтаксис языка С++ используется в выражениях Qx только для удобства читабельности и редактирования кода. С++ выражения не транслябельные в портабельный SQL не допускаются (за исключением неизбежных оптимизационных хаков записанным в явном виде в осмысленный ущерб портабельности).
Qx позволяет вынести формирование любого элемента запроса в отдельную функцию, сделать этот процесс модульным, скрыть детали и сделать их взаимозаменяемыми.
Более того, реально интегрировать в единый эффективный запрос запросы из нескольких модулей, каждый из которых инкапсулирует свой фрагмент реляционной БД. При этом модулям не нужно ничего знать о структуре данных друг друга. Это позволяет не жертвовать критичной для развиваемости инкапсуляцией данных в ООП приложениях применяя в них реляционное хранилище.
Контроль типов С++ используется для проверки элементов SQL-выражений на совместимость, а qx-классы, позволяя сформировать интегральное выражение, не дают возможность получить скрытые в нем ссылки на таблицы и колонки чужого модуля.
Например две инкапсулированные независимо БД - одна с логом посетителей сайта (формирует нижеследующий запрос), другая с классификатором посещений (rCategoryDb) поставляющая для запроса необходимый классифицирующий фильтр:
// call rCategoryDb database to build classification part for our query qxbool qxboolCategoryFilter = rCategoryDb-> GetCategoryFilter( // pass our private visit parameters encapsulated // to rCategoryDb Qx fromVisit->x_sBrowser, Qx fromVisit->x_sIpAddress, Qx COALESCE(fromUser->x_sGroup, "Guest")); // merge encapsulated categorization filter into our query rQuery->x_qxboolWhere = Qx (qxboolCategoryFilter) && !fromUser->x_bHidden;
Запрос формирует БД посетителей и мы видим здесь ее поля и таблицы. Но их не видит инкапсулятор rCategoryDb. В свою очередь мы не видим здесь внутренней структуры rCategoryDb.
Два модуля сформировали один запрос без нарушения инкапсуляции подвластных им фрагментов БД.
Qx выражение начинается с ключевого слова Qx. Оно включает идентификаторы и символы операций в синтаксисе С++ которые трансформируются Cx в соответствующий синтаксис SQL. Завершается выражение точкой запятой (или закрывающейся скобкой в которую оно может быть заключено).
Ключевое слово Qx обязательно пишется на своей отдельной строке (особый случай когда все выражение обернуто в скобки, тогда открывающаяся скобка на отдельной строке вместе с Qx: "(Qx").
Все вложенные выражения имеют отступы по ПРД.
Отклонения в будущих версиях будут контролироваться как ошибки компиляции.
Каждое Qx ... ; выражение для компилятора превращается в один из классов qx-выражений в зависимости от своего типа. Это обеспечивает статический контроль правильности последующего слияния выражений.
По типам qx-классов выражения подразделяются как по допустимости их применения в тех или иных контекстах и операторах SQL (во WHERE, GROUP, ORDER и т.п.) так и по типам переменных которые в них участвуют:
Выражение Qx инкапсулированное в объекте одного из этих классов
Это дает возможность передавать такие выражения наружу из инкапсулирующего кода без риска того, что их приватные внутренности будут вскрыты в нарушение инкапсуляции.
IS (NOT) NULL in Qx пишется по аналогии с SQL.
Портабельный SQL LIKE применяется к строковым выражениям.
Все чувствительно к регистру. Как в С++. Не как в SQL.
Так как Qx поддерживает списки SQL выражений в форме
Qx expr, expr, expr
то при вызове функций может возникнуть неоднозначность:
f(Qx a, b, c)
Здесь b и c это параметры функции f() или часть выражения Qx?
Эта делема разрешается в пользу Qx. То есть:
f((Qx a, b, c))
а не
f((Qx a), b, c)
Так что если qx-выражение не в последнем параметре функции то его надо заключать в скобки. Если же оно последнее, то не нужно - и этим фактом пользуются все стандартные функции принимая JOIN ON выражение в последнем параметре:
fromDetect. Join( x_rRoot-> _x_dblistDetect, key(), Qx fromDetect->x_keyCategory == fromCategory->GetKey());
Если бы дальше через запятую был еще один параметр то он прилепился бы к Qx и пришлось бы исользовать скобки
MyFunction( x_rRoot-> _x_dblistDetect, key(), (Qx fromDetect->x_keyCategory == fromCategory->GetKey()), anotherParameter);
Эта категория в данный момент пуста.