Qx1

  !   Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам)

Синтаксис Qx предназначен для безопасного и одновременно юзабильного составления портабельных SQL запросов из C++Cx транслирует Qx выражения в низкоуровневый С++ код, составляющий текст SQL-запроса и осуществляющий биндинг локальных переменных С++ c запросом. Все это typesafe с максимальным статическим контролем.

Особенности
Не "встроенный SQL"

Важно понимать что 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 для иерархий объектов. (Эта установка не исключает естественно необходимых механизмов кеширования в памяти осуществляемого на разных уровнях инкапсуляции хранилища и подстановки данных из нереляционных источников на уровне драйверов СУБД или прикладных хранилищ.)

Портабельный SQL

Не надо воспринимать Qx как магическую абстракцию скрывающую SQL и все особенности, проблемы и преимущества реляционной СУБД. SQL надо знать, реляционную структуру надопроектировать. Цель применения Qx - это составление стандартного SQL запроса, который на низком уровне будет представлен в текстовой форме для последующей интерпретации бакендом и удобный биндинг С++ переменных на каждой итерации запроса.

Нужно четко различать

  • C++ код, формирующий запрос
  • и 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 и т.п.) так и по типам переменных которые в них участвуют:

  • class qxbool- логическое выражение допустимое практически во всех контекстах и в рамках других выражений, за исключением как аргумент вычислительных операций
  • class qxinteger- целочисленное выражение (32/64 бита не уточняется)
  • class qxfloating- операции с плавающей точкой
  • class qxstring- строки, включая path, srtf, sloc
  • class qxblob- sbuf
  • class qxkey- ключ key
  • class qxmany- список выражений через запятую для GROUP BY
  • class qxstat- выражение или список таковых, включающих агрегатные функции
  • class qxsort- список выражений для ORDER BY включающих спецификацию ORDER(E_Order_Ascending/Descending) после каждого выражения

Выражение Qx инкапсулированное в объекте одного из этих классов

  • не может быть изменено
  • не может быть прочитаноиз объекта (ни в виде текста ни в виде отдельных элементов)

Это дает возможность передавать такие выражения наружу из инкапсулирующего кода без риска того, что их приватные внутренности будут вскрыты в нарушение инкапсуляции.

Скобки
Списки
Биндинг
Ссылки на таблицы
Операции
Логические
IS NULL / IS NOT NULL

IS (NOT) NULL in Qx пишется по аналогии с SQL.

Вычислительные
Строковые
LIKE

Портабельный SQL LIKE применяется к строковым выражениям.

Функции
Агрегатные
Слияние qx-выражений
Регистр

Все чувствительно к регистру. Как в С++. Не как в 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);

Эта категория в данный момент пуста.