Призрачные SQL запросы

SKY / WINGS / FIRST /
GHOST1
В большинстве веб-приложений, потенциально существует множество запросов к БД, для которых, в каждый момент времени, запрос полностью не определен, но известна только лишь его часть. Например, запись в таблицу сессий посетителей сайта: вы можете отметить в БД время последнего клика посетителя, но в этот момент времени, может быть не известно, что пользователь переключил язык интерфейса и этот факт нужно также отметить в БД в той-же таблице и в том же её ряду. Нехорошо в таких случаях делать два запроса к БД, когда можно сделать только один с помощью функционала "GHOST - призрачные SQL".

Такие "отложенные" SQL запросы, известны и используются, например в реальных ORM, но не нужно создавать ORM, давайте рассмотрим, как организован этот функционал в коде первого крыла SKY с помощью обычных запросов SQL с шаблонами.

Итак, SQL который требуется выполнить, мы будем "накапливать" (первый функциональный аспект), а реальное выполнение запроса SQL будет, например, происходить автоматически, в конце работы скрипта, в хендлере, обращение к которому организовано с помощью функции register_shutdown_function(), но не всегда, а только если это удобно программисту. Иногда удобно организовать генерацию реального запроса к БД вручную, другим способом. Однако такая автоматическая генерация запроса используется в коде coresky для данных сессий, авторизованных пользователей и конфигурации веб-приложения.

Функционал "Призрачные SQL запросы" имеют и второй функциональный аспект: память_SKY. Память SKY - это специальный способ хранения множества разных значений в одной ячейке БД, связанный с функционалом GHOST. GHOST функционал может создавать реальные запросы к БД, а может и не создавать, если в БД уже имеются соответствующие данные, собственно поэтому функционал именуется призрачным. Кроме того, управление памятью SKY, часто удобно "прикрутить" к магичеcким методам __get(), __set(). Давайте отойдем от абстракций и рассмотрим работу функционала на реальном примере. Взгляните на код:

001
$user->v_useragent 'CORE.SKY';

Если такой код выполняется первый раз для текущей сессии, то в callback хендлере register_shutdown_function() будет выполнен реальный SQL запрос INSERT или UPDATE, который сохранит значение `CORE.SKY` в БД и при последующих запусках скрипта (кликах пользователя), это значение будет доступно для чтения из переменной $user->v_useragent. Как все работает: класс USER имеет магические методы __get() и __set() для работы с такими переменными. Для случая записи запускается __set(), с помощью префикса "v_" метод детектирует, что текущая переменная это память SKY относящаяся к сессиям посетителей. Далее функция SKY::save(..) сохраняет информацию в массиве `SKY::$mem`, который в последующем будет использован для генерации реального запроса INSERT или UPDATE. Если во время выполнения кода, переменная $user->v_useragent уже содержит значение `CORE.SKY`, SQL UPDATE выполнен не будет!

Для работы с призрачными запросами, в классе SKY, имеются три метода: SKY::ghost(..), SKY::here(..), SKY::save(..). Код первого метода довольно прост, приведу полностью:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
<?
class SKY
{
..
static function &ghost($char$original$sql ''$flag 0) {
    SKY::$mem[$char] = [$flag$flag null $original$sql, []];
    if ($sql)
        trace('GHOST SQL: ' . (is_array($sql) ? end($sql) : $sql), false1);
    if ($original) foreach (explode("\n"unl($original)) as $v) {
        list($k$v) = explode(' '$v2);
        SKY::$mem[$char][3][$k] = unescape($vtrue);
    }
    return SKY::$mem[$char][3];
}
 

В коде coresky широко используются одно-буквенные префиксы и постфиксы. Как можно было заметить ранее, GHOST запрос к таблице `visitors`, для организации работы с памятью SKY, происходил из-за префикса `v_` . Так каждый тип GHOST имеет "свою букву". Занятые буквы в коде coresky:

001
002
003
004
005
006
s - system conf
a - admin conf
n - cron conf (console)
v - visitor (session)
u - user (registered)
i,j,k - Language data

Эта буква передается первым параметром в SKY::ghost(..) и является индексом массива SKY::$mem относящегося к конкретному типу GHOST. Восемь букв уже занято ядром, 26 - 8 = 18 доступно для приложений. Как показывает практика, в сложных приложениях вы можете использовать 3, максимум 5 букв. Так что имеется резерв и для развития ядра и для пользовательских приложений хватает.

Второй параметр $original - запакованные данные памяти SKY, строка в формате:

001
002
003
004
ключ1 значение1_с_кодированными_переносами_строк
ключ2 значение2_с_кодированными_переносами_строк
..
ключN значениеN_с_кодированными_переносами_строк

Такая строка обычно считывается из таблицы в БД, колонки имеющей тип `text`, `mediumtext`. Для случая сессий пользователей это колонка "vmemo text" в таблице `visitors`. Если вы планируете использовать подобную колонку в некоторой таблице, которая будет использовать функционал GHOST, вы должны следовать соглашению и дать имя такой колонке Xmemo, где X - буква которая выделяется для функционала. Метод SKY::ghost(..) можно также просто использовать для распаковки данных хранящихся в таком формате.

Каждому типу GHOST выделяется массив содержащий 4 элемента. Первый - флаг, второй - исходная строка, третий шаблон SQL запроса, четвертый - распакованные данные. Третий бит флага используется для внешнего сброса $original в null. Алгоритм таков: если вы произвели запись первого типа устанавливается бит 1, если второго - второй бит. В методе SKY::here(..) сначала проверяются первый и второй бит, если они оба не установлены (не было записи), обработка данного типа GHOST прекращается. Но если запись была, ключи-значения упаковываются в строку и сравниваются с $original и только если строки отличаются, генерируется и выполняется реальный SQL запрос.

Третий параметр функции - $sql собственно шаблон SQL запроса, который используется для генерации реального SQL. Шаблоны бывают двух типов: для применения в функции sqlf(..) или же sql(..) соответственно первому и второму биту флага. Если вы планируете записывать только в память SKY (колонку Xmemo) используйте шаблон на основе функции sqlf(..). Для примера смотрите код метода `$sky->load()` в файле `main/sky.php`, если же и другие, произвольные колонки, используйте второй формат. Для примера смотрите использование SKY::ghost(..) в `USER::__construct()` в файле `main/heaven.php`. В качестве шаблона можно также передать пустую строку, это значит, что запрос не будет производиться автоматически в конце выполнения скрипта, и вы планируете организовать его вручную.

Используя метод SKY::save($k, $v = 0, $char = 's') можно сохранять данные в массиве SKY::$mem, которые в методе SKY::here(..) будут использованы для генерации и выполнения реального SQL запроса. Варианты использования:

001
002
003
004
SKY::save(string, scalar) - сохранить одну пару ключ-значение (память SKY)
SKY::save(array) - много пар ключ-значение (память SKY)
SKY::save(string, null) - удалить из БД одну пару, похоже на flash хранение (память SKY)
SKY::save(null, array) - провести запись в другие колонки (кроме памяти SKY), тип записи 2

Вызвав SKY::save() из магического метода __set() запись в БД можно сделать вообще тривиальной, как в примере выше для сессий посетителей.

Кстати, в примере выше, несмотря на то что при повторном вызове SQL сгенерирован не будет, все-же более разумно писать:

001
$user->v_useragent or $user->v_useragent 'CORE.SKY';

Таким образом не будет даже установлен флаг, что не приведет к упаковке данных для последующего сравнения с оригинальной строкой. В методе SKY::here(..) будет в первую очередь произведена проверка флага, так как он не установлен, сразу же прекратится вычисление данного типа GHOST, это более эффективно.

Резюмируем



Призрачные SQL запросы - классный и очень востребованный функционал в любом веб-приложении. Он делает программирование проще и эффективнее. Этот функционал применяется для создания "умных" запросов типа INSERT, UPDATE или REPLACE и применим, как правило, для одного ряда одной таблицы. Перед запуском SKY::ghost часто необходимо прочитать с помощью запроса SELECT этот ряд данных. В основном, призрачные запросы, удобно использовать для конфигураций.

Достоинства

1. Код легко привести к виду, когда вместо нескольких SQL запросов, будет всего один. При этом происходит проверка, если запрос можно не выполнять, он не выполняется вообще.

2. Не нужно создавать новые колонки для таблиц (с помощью ALTER TABLE..) чтобы добавить новую переменную (либо делать INSERT для новых рядов конфигурации), когда вы развиваете свое приложение, - просто используйте "память SKY", это намного проще.

Недостаток

1. Невозможно создать индексы для данных типа "память SKY" или делать поиск по значению в БД. Но для конфигураций, это и не нужно.
опубликовано ENERGY - 7 Mar 2019 07:58 GMT
последнее редактирование - 30 Mar 2019 14:58 GMT
 +  0  -  комментировать