В некоторых случаях, при использовании хранимых процедур, ну очень хочется иметь механизм аналогичный псевдотаблице dual из Oracle. Как сделать то же самое в Cache?Dual - это условная псевдотаблица, к которой можно обратиться с операцией select и всегда получить одну строку. Операции update, delete и insert к ней не применимы (генерируют ошибку) или не имеют эффекта. Используется в операции обращения к хранимой процедуре, которая возвращает результат, чтобы использовать его как результат select. В штатные средства Cache, насколько я знаю, такой механизм не входит. Попробуем его выполнить самостоятельно.
Основная идея - это создать таблицу средствами cache, чтобы она принималась, как самая обычная, но при этом операции delete, insert и update ни к чему бы не приводили, а операция select всегда возвращала одну строку. Содержание этой строки неважно, пусть будет ID, который создается в Cache для любой таблицы автоматически.
Также, хотелось бы, добиться качественного решения, чтобы никакие системные администраторы или тестеры или пользователи не смогли повлиять на работоспособность таблицы ни по забывчивости, ни по ошибке. Основные требования:
- после импорта и компиляции класса псевдотаблица должна быть работоспособна
- при работе процессов, которые ее используют, не должно быть взаимных конфликтов
- из таблицы нужно доставать ровно одну запись, независимо от действий различных процессов
Я экспериментировал с каше версии 5.2.3 и 5.0.15. В обеих версиях вариант получился работоспособным. Вот такой вот класс, как он выглядит в Cache Studio при включенной опции "показывать способ хранения":
Class User.Dual Extends %Persistent [ ClassType = persistent, Not ProcedureBlock, SqlTableName = DUAL ] { ClassMethod storage() As %String { k zzzzdual s zzzzdual(1,0)=$lb("User.Dual") q 1 } <Storage name="Default"> <Data name="DualDefaultData"> <Value name="1"> <Value>%%CLASSNAME</Value> </Value> </Data> <DataLocation>zzzzdual($$zstorage^User.Dual.1())</DataLocation> <DefaultData>DualDefaultData</DefaultData> <IdLocation>zzzzdual($$zstorage^User.Dual.1())</IdLocation> <IndexLocation>^User.DualI</IndexLocation> <StreamLocation>^User.DualS</StreamLocation> <Type>%Library.CacheStorage</Type> </Storage> }Здесь не используются ни одно свойство, ни один индекс, или потоковое свойство, поэтому, указание в способе хранения опций IndexLocation и StreamLocation - чистая формальность.
Идея такой организации состоит в том, что при обращении за данными мы вынуждаем движок каше вызвать сначала нашу функцию (storage), и использовать переменную, которой она оперирует. Сначала переменная очищается, потом создается структура, изображающая одну запись. Чтобы была вызвана наша функция, указываем первым индексом, что вызвать. Поэтому, хранение данных каше ожидает в виде подиндексов переменной zzzzdual(нечтонаше), например
zzzzdual("какая-то строка",ID1)=первая запись zzzzdual("какая-то строка",ID2)=вторая запись zzzzdual("какая-то строка",ID3)=третья записьВ моем варианте, в переменной zzzzdual в качестве
Даже, если какие-то механизмы каше смогут создать ид новой записи (хотя мы можем вмешаться и в этот процесс) и дописать еще один узел, то мы, все равно, не дадим шанса прочитать ничего, кроме одной записи, а также, если будет произведено удаление, даже, всех локальных переменных, мы все равно снова восстановим структуру, по которой пройдется $order() при операции select.
Основная критика решения касается выражения
- Если есть метод Method, написанный программистом, то в int коде ему соответствует метка zMethod.
- int код размещается в рутинах с именем, производным от имени класса.
- zzzzdual(##class(User.Dual).storage()) - вызов через классовый синтаксис
- zzzzdual($zobjclassmethod("User.Dual","storage")) - вызов через $zobj функцию
USER>w $zv Cache for Windows NT (Intel/P4) 5.0.15 USER>d $System.SQL.Shell() >>select * from dual (1)>>go 0 >>exit USER>Теперь, можно спокойно обращаться к хранимым процедурам в средах, которые не очень хорошо к ним относятся (например путают call и execute). Достаточно написать
select stored_proc_name(params) from dualБолее того, поскольку используется форма обращения через select, возможно строить view, в том числе использующие хранимые процедуры:
create view dual2 as select * from dualПосле компиляции класса я получил MAC код (User.Dual.T1.MAC) с такими метками:
%delete(%rowid,%check) s SQLCODE=-115,%msg="Cannot DELETE from a Read-Only table" q %insert(%d,%check) s SQLCODE=-115,%msg="Cannot INSERT into a Read-Only table" q "" %update(%rowid,%check,%d) s SQLCODE=-115,%msg="Cannot UPDATE a Read-Only table" qМне, конечно, приятно, что компилятор классов прочитал мои мысли и решил запретить модификацию данных в таблице, но почему он это сделал на самом деле, я еще не знаю.
Комментариев нет:
Отправить комментарий