RexxDB. База данных на Rexx.

Автор: Тарасов А.Е.

Консультант: Кузнецов В.Е.

Дата: 07.2015

Скачать статью в формате Word.

Скачать архив.

Скачать необходимые дистрибутивы и библиотеки.

Любые программы, так или иначе, работают с данными и управляют ими. В настоящее время я веду несколько проектов на языке Rexx, задача которых в той или иной степени обрабатывать и отображать данные. Изначально решаемые задачи были примитивные, ограниченные сбором и выводом данных, а развитие не предполагалось. Поэтому для простоты все хранилось в текстовых файлах. Однако проекты все-таки потихоньку развивались, задачи усложнялись, аппетиты росли, что потребовало введения понятия «база данных». Язык Rexx не обделен средствами работы с sql и mysql базами. Но все существующие базы данных строятся на основе типизации данных, тогда как в Rexx отсутствуют понятие «тип переменной», что, на мой взгляд, приводит к дисбалансу логики программирования на Rexx. Поэтому я задумался о возможности написания базы данных на самом языке Rexx. Сел за эксперименты, о результатах которых я и хочу рассказать.

Первое что хочется отметить – эксперименты завершились успешно. Удалось реализовать основной набор функций по работе с базой данных полностью на языке Rexx.

В настоящий момент реализованы следующие команды по работе с данными: select, insert, update, delete. При этом не существуют ограничений на добавление новых команд. А так как все написано на языке Rexx, можно без особого труда вносить изменения в уже существующие команды.

 

Структура базы данных

 

Все таблицы базы данных должны располагаться по единому пути (в одном «корневом» каталоге). Каждая таблица базы представляет из себя подкаталог, где расположен файл описания параметров таблицы со стандартным именем «Description.ctl». В качестве имени таблицы используется имя подкаталога. Все данные таблицы хранятся только в этом подкаталоге и могут иметь любую организационную структуру.

Подпрограммы работы с базой данных представляют собой самостоятельные для каждой команды скрипты, располагающиеся в корневом каталоге базы данных.

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

Сейчас можно использовать три структуры внутренней организации текстовых файлов базы, это:

  • Командный – com;
  • Табличный – tab;
  • Структурированный по дате – date (является специализированным развитием типа tab).

Рассмотрим перечисленные структуры подробно.

 

Командная структура (COM)

 

Минимальная организационная структура - файл, внутри которого все данные храниться через присвоение. В одной таблице базы имеется большое количество таких файлов. По сути это стандартные конфигурационные файлы, что сохраняет возможность легкой ручной правки. Его удобно использовать для хранения небольших объемов данных в несколько тысяч записей. Особенно удобна такая структура для хранения разного рода конфигураций и списков параметров.

 

Рис 1. Пример файла с командной структурой.

 

Табличная структура (TAB)

 

Минимальная организационная структура – строка, в которой данные записаны через разделитель. Править руками данные можно, но при большом объёме данных - затруднительно. Как правило, таблица содержит один файл, но возможно его разбиение на произвольное количество файлов. В данном формате удобно хранить любые данные, причем практически любого объема. Ограничения накладывают только возможности операционной системы и здравый смысл.

 

Рис 2. Пример файла с табличной структурой.

 

Организация по дате (DATE)

 

Это развитие табличной структуры, но здесь данные хранятся не в одном файле, а на каждую дату создается отдельный файл. По этой причине не привожу пример. Мне данная структура понадобилась для хранения ежедневно поступающих с датчиков данных. И зарекомендовала себя весьма положительно. Так же она иллюстрирует возможности по расширению форматов хранения данных.

 

Работа с базой данных

 

Теперь рассмотрим, как же все это работает. Предполагается три способа организации работы с базой данных:

  1. Через постоянно запущенный гипервизор «Сервер данных», который отслеживает появление запросов, и отрабатывает их. Так же он способен запускать таймеры, организовывать сбор данных из различных источников и вести дополнительную системную работу. В таком режиме возможна отработка запросов по сети. В нем же реализована универсальная возможность просмотра и редактирования данных таблиц оператором.
  2. Командный режим. Гипервизор запускается из командной строки, а по завершению отработки текущих запросов отключается.
  3. Через функции. В тело прикладной программы добавляются несколько процедур, которые обеспечат работу с базой. Всего 11 функций. По сути, программа начинает напрямую работать с базой, что имеет свои плюсы и минусы. Гипервизор при этом не обязателен и служит только для ручной правки базы.

 

Общение с гипервизором «Сервер данных» и запрос данных из базы происходит через технические файлы. Для запроса данных по заранее известному пути формируется файл с расширением «sql», а по окончанию работы по тому же пути формируется файл с тем же именем и расширением «reply». Весь процесс спрятан в процедуру под названием «SQL». Для обращения к базе данных необходимо вызвать эту процедуру с указанием в качестве первого аргумента описания запроса.

При работе через гипервизор «Сервер данных» к базе можно обратиться из любого языка программирования и получить возврат в текстовом файле. При программировании на языке Rexx есть вкусность в виде автоматической загрузки возвращенных данных в память (в переменные и массивы). Этот режим используется по умолчанию.

Как файлы базы данных могут иметь несколько форматов организации, так и возврат может иметь следующие форматы (все они доступны для таблиц любой организации):

  • Командный – com. Файл возврата «reply» содержит присвоения значений. При загрузке в память получаем разветвленный массив, где каждый элемент храниться под своим именем. (Пример: «Основа».«Поле_Index».«Столбец») Массив содержит дополнительные технические элементы: «.LIST» - здесь через пробел перечислены значений полей «INDEX» всех возращенных элементов; «.STRUCTURE» - здесь через пробел перечислены название всех возращенных колонок. Эти два элемента позволяют без проблем организовать цикл по разбору возращенных данных. По умолчанию основа для имени массива «SQL.», если нужна другая основа, её необходимо указать в качестве второго аргумента при вызове функции «SQL»;
  • Табличный – tab. При загрузке в память то же получаем массив, но организованный классическим для функций Rexx способом. Под индексом «.0» элементом храниться общее количество элементов, а сами элементы содержат строки из записанных через разделитель значений. Здесь так же имеется технический элемент «.STRUCTURE». По умолчанию основа для имени массива «SQL.», если нужна другая основа, её необходимо указать в качестве второго аргумента при вызове функции «SQL»;
  • Линейный – line. В случае если из таблицы запрашивается значения одной колонки, их можно вернуть напрямую в возврате функции «SQL» записанными в строчку через разделитель. Этот возврат позволяет значительно упростить алгоритмы прикладной программы, а так же осуществлять вложенные запросы к базе данных.

 

Реализованные команды

 

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

 

Select

SELECT <Список столбцов> FROM <Название таблицы> [PASSWORD] [USER] [WHERE <Список условий>]

Insert

INSERT INTO <Название таблицы> [PASSWORD] [USER] [<Имя столбца>,…] VALUES [<Значение>,...]

Delete

DELETE FROM <Название таблицы> [PASSWORD] [USER] [WHERE <Список условий>]

Update

UPDATE <Название таблицы> [PASSWORD] [USER] SET <Список присвоений> [WHERE <Список условий>]

 

Где:

Список столбцов - перечисление через запятую названий столбцов;

Название таблицы – имя таблицы, к которой идет обращение;

Список присвоений – перечисление через запятую присвоений новых значений вида «Имя столбца» = «Новое значение»;

Список условий – перечисление через запятую условий для выполнения команды. Условия проверяются в конструкции «INTERPRET IF», то есть должны удовлетворять правилам синтаксиса команды IF языка REXX. Допускаются использования всех стандартных функций. Для каждой строки все условия проверяются поочередно, команды выполняется при истинности хотя бы одного из них (логическое или). Если нужна комбинация условий, их не обходимо объединять по правилам команды IF языка REXX. Если первый символ условия - знак «#», то такое условие будет выполняться как команда по правилам языка REXX. Все внутренние переменные скриптов начинаются с «sql», что позволяет не опасаться пересечения имен переменных. Такой подход ранее хорошо себя зарекомендовал в особо тяжелых случаях.

PASSWORD и USER – Пароль и имя пользователя. Передаются всегда. Если не указаны напрямую, то Password передается пустым, а в USER помещается имя текущего пользователя. Используются для организации прав доступа к таблице.

Значение – значения для соответствующего столбца. В конфигурационном файле таблицы «Description.ctl» можно также указать значения по умолчанию, поэтому нет жёсткой необходимости каждый раз прописывать все столбцы при записи в новую строку.

 

В запросах существует возможность влиять на их отработку, путем изменения ряда параметров:

  • TRM – изменяет разделить значения для возврата на текущем запросе. По умолчанию используется разделитель таблицы.
  • FILEEXT – Расширение просматриваемых файлов таблицы.
  • SCANSUBDIR – Включает/Отключает рекурсивный просмотр подкаталогов в таблице.
  • LIMIT – Ограничитель количества возвращаемых строк. Может содержать одно или два числа (через пробел). Второе число определяет максимальное число возвращаемых строк а первое число указывает на номер строки перед первой возвращаемой. LIMIT=’5 10’ вернет строки с порядковыми номерами 6-15. Если указано только одно число, то оно показывает максимальное количество возвращаемых строк. LIMIT=’5’ вернет первые 5 строк (эквивалентно LIMIT=’0 5’).
  • LOG – Включает/Отключает логирование для текущего запроса.
  • INLINE – Включает/Отключает возврат в строку для текущего запроса, в случае если запрошены значения только одного столбца.
  • RESPONSE – Включает/Отключает возврата результата выполнения. При отключении не ждем окончания работы скрипта и он ни чего не возвращает. Select – игнорируется.
  • LINK – Включает/Отключает ссылочный возврат. При включении загрузки в память возврата происходить не будет, а вернется полный путь к файлу с данными возврата.
  • TABTYPE – Указывает желаемый формат файла возврата (TAB/COM). Если не указывать, будет использован формат таблицы.
  • DATEDO – Начальная дата для просмотра данных в таблицах организованных по дате (DATE).
  • DATEEND - Конечная дата для просмотра данных в таблицах организованных по дате (DATE).

 

Указанные параметры прописываются  в поле WHERE или VALUES.

Из-за организационных и прочих сложностей, пока не реализовал сортировку таблиц перед возвратом. Но подготовил две функции для сортировки загруженных данных возвратов, позволяющих отсортировать массив по значению любого поля:

  • ArraySort – сортировка массивов с возвратом типа COM.
  • ArraySortTab - сортировка массивов с возвратом типа TAB.

 

Контроль доступа

 

Параметры доступа устанавливаются индивидуально для каждой таблицы в файле конфигурации таблицы «Description.ctl». Реализованы следующие режимы:

0 – без проверки уровня доступа;

1 – проверка доступа на уровне гипервизора;

2 – проверка доступа на уровне таблицы;

3 – двойная проверка доступа на уровне гипервизора и таблицы.

 

Пароль доступа и список имен пользователей, которые имеют право доступа к таблице, так же хранятся в конфигурационном файле «Description.ctl». Пока реализован только контроль полного доступа, но планируется отдельно реализовать контроль доступа в режиме чтения.

В случае если пользователю отказано в доступе к таблице, возвращается сообщение об ошибке.

 

Система логирования

 

Предусмотрена система логирования, которая так же управляется в конфигурационном файле «Description.ctl» для всей таблицы, и может устанавливаться для каждого запроса отдельно.

Лог файл не единый, а создается на каждый день в подкаталоге «LOG», расположенном в каталоге таблицы. Что позволило не заботиться о длине файла и легко обнаруживать возникновение ошибок.

Если логирование включено, происходит запись обо всех действиях с таблицей. Кроме того запись в log происходит при возникновении любых аварийных ситуаций, даже если логирование в конфигурации таблицы или в запросе отключено.

При успешном завершении скрипта в лог пишется:

  • дата и время записи,
  • время работы скрипта,
  • текст запроса.

 

При аварии в лог пишется:

  • дата и время записи,
  • код ошибки,
  • описание ошибки,
  • номер строки скрипта в которой произошла ошибка,
  • содержание строки,
  • время работы скрипта до момента возникновения аварии,
  • текст запроса.

 

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

Дополнительной защитой служит контроль работоспособности скрипта. Если скрипт зависает и перестает отвечать, гипервизор через контрольное время генерирует аварийное событие «База данных или скрипт не отвечает», о чем уведомляется пользователь. Но такое пока происходило только на этапе отладки. Мониторинг ситуации происходит в цикле, размер которого по умолчанию 1000 ротаций, что примерно равно 25 секундам. Управлять циклом можно путем передачи его размера в качестве третьего параметра функцию «SQL». Если передать 0 контроль для данного запроса отключается.

 

Просмотр и редактирование таблиц в гипервизоре

 

Изначально передо мной стояла двойственная задача, с одной стороны мне нужен был механизм для хранений значительных объемов данных, с другой мне нужен был удобный инструмент для хранения различных конфигураций и настроек. Решая вторую задачу, я ввел в систему универсальную возможность просмотра и редактирования данных в таблицах через гипервизор. Видимостью таблицы в гипервизоре можно так же управлять в конфигурационном файле «Description.ctl». Это позволяет предотвратить случайную порчу таблиц, предназначенных для хранения больших массивов данных.

Рис 3. Внешний вид окна просмотра/редактирования таблицы

 

Для организации комфортной работы доступны следующие формы:

  • Entryfield – строка ввода значения.
  • ComboBox - строка с выпадающим меню выбора, с данными колонки любой таблицы базы данных (Рис 4).
  • Button - кнопка для вызова пользовательских функций.
  • WinSqlSelector - кнопка для открытия окна выбора содержания типа Selector (Рис 5).
  • WinDirSelect - кнопка для окна выбора каталога, без дополнительных данных (Рис 6).
  • При желании можно задействовать любые стандартные (для RexxDW) элементы формы, только нужно правильно описать поля Options, Out, In.

Рис 4.

 

Рис 5.

Рис 6.

 

Немного о быстродействии

 

Быстродействие программ на интерпретирующих языках, таких как REXX сильно зависит от железного фактора и от загрузки системы. Я программирую на ноутбуке Dell с двухъядерным процессором 2.16 Мгц. И приведу немного статистики.

Тесты показали, что время отработки одной строки в команде SELECT для таблиц типа TAB для возврата без условий того же типа составляет ~0.00003 сек, а для возврата типа COM ~0.00008 сек. Следует помнить, что по умолчанию все возвраты загружаются в память, и на это так же требуется время. Если загрузка данных возврата в память не требуется, это необходимо указывать в запросе, в таком случае вернется полный путь к файлу возврата.

Практика работы не выявила сильной зависимости скорости работы от формата организации данных в таблице. Но обнаружена значительная зависимость скорости работы скрипта от формата возврата. Например, если время выборки без условий всех строк (для теста было взято немного более 400.000 строк) из таблицы с организацией типа TAB с возвратом типа TAB составляет около 16 секунд (с учетом времени на загрузку в память). Но если из этой же таблицы потребовать возврат типа COM, время только работы скрипта составит уже около 50 секунд, а время загрузки данных в память увеличится примерно до 450 секунд. Такая значительная разница вызвана, главным образом, следующим:

  1. При возврате типа COM формируется и записывается в файл дополнительная переменная LIST, содержащая индексы всех элементов для доступа к ним. В текущем примере получаем строчку с более чем 400.000 слов.
  2. При возврате типа COM каждое значение элемента записывается в своей строчке. В текущем примере получаем 2.500.000 срок. А так как для загрузки в память возврата типа COM, необходимо обработать каждую строку в отдельности (сделать Interpret()), подобный цикл затягивается на 450 секунд.

 

Выход:

  1. Для хранения значительных объемов данных следует использовать таблицы типа TAB и этот же формат применять для возврата запросов к таблицам.
  2. Таблицы типа COM удобны в первую очередь для хранения всяческих настроек или описаний. В этом случае использование формата таблицы COM оправдано. ИМХО формат COM проще в дальнейшей работе, так как не требует дополнительных операций по разбору ответа и можно сразу после получения возврата начинать с ним работать. Возврат типа COM не следует использовать при больших объемах возврата. Точные границы для каждого случая лучше устанавливать экспериментально. Если такой возврат все-таки необходим, следует запросить ссылочный возврат и самостоятельно провести его разбор – так получится быстрее.

 

Планы на будущее

 

Куда же без них. :) Вот что планирую добавить в систему в ближайшем и не очень будущем:

  1. Разграничение прав доступа к таблицам, на «полный доступ» и «только чтение». Пока реализован только полный доступ.
  2. Перевести взаимодействие отдельных частей системы на использование механизма именованных каналов. Сейчас все взаимодействие происходит через временные файлы. Надеюсь на хоть не большое, но увеличение быстродействия и уменьшение нагрузки на диск.
  3. Доработать работу с гипервизором по сети. Сейчас реализовано взаимодействие только через сетевые папки, хочу научить систему работать так же через IP-сокетсы и попробовать обратиться к ней через интернет.
  4. По мере надобности буду развивать существующие команды, и добавлять новые.

 

Подводим итоги

 

В процессе подготовки статьи я старался, не уходя в технические дебри, дать представление о том, что же у меня получилось. А теперь давайте попробуем вообще свести все к ряду тезисов:

  1. Система получилось простая и компактная, способная работать без каких либо дополнительных программ. Одновременно с этим считаю, что потенциал развития у неё есть.
  2. Система изначально создавалась многоплатформенной, что влечет дополнительные плюсы. По крайне мере для меня.
  3. Система легко расширяемая. Как в части форматов баз данных - теперь в базу данных можно превратить любую структуру данных. Так и в части разработки новых команд. Так как все написано на языке Rexx, систему легко модернизировать под любые задачи.
  4. Система позволяет хранить не только данные, но и самые разнообразные настройки. А так же она позволяет редактировать их в «графической» оболочке. Для меня это важно, так как мой напарник по одному проекту весьма поверхностно разбирается в компьютерах, и теперь я смогу свалить на него ряд задач.
  5. Система получилась стабильной и устойчивой к любым нестандартным ситуациям. Несмотря на то, что я стремился максимально уменьшить размер кода, удалось организовать вполне адекватную «защиту от дурака».
  6. Не могу рекомендовать систему для каких либо глобальных проектов, но для небольших или средних она может подойти идеально.

 

Описание архива

 

Теперь поговорим, как же все это запускается.

 

1. Для OS Windows, любой версии.

Потребуется установить язык программирования Rexx. В качестве интерпретатора языка я использую Regina Rexx (последнюю версию 3.9 брать здесь). Так же потребуется библиотека графического интерфейса RexxDW, последнюю версию на сегодня 2.1 брать здесь. Что бы избежать проблем с совместимостью библиотек рекомендую ставить 32-битные версии. Собственно здесь все просто, программы самоустанавливающиеся, и дополнительных действий не нужно.

 

2. Для OS/2 (eCS), любой версии.

Разумеется, потребуется последняя версия RexxDW (брать здесь). Но она работает только с Regina Rexx, так что параллельно со стандартным потребуются установить этот интерпретатор (брать здесь). Установить почти так же просто, из обоих архивов нужно все файлы exe и dll скопировать по путям, прописанным в config.sys.

 

Распаковываем.

Архив с «RexxDB.rar», содержит папку «BaseData» - пример базы данных от одного из моих проектов, и папку «СерверДанных» - содержит примеры работы с базой данных:

  • Server.ctl – начальные настройки гипервизора «СерверДанных».
  • СерверДанных.rexx – собственно сам гипервизор.
  • СерверДанныхDos.cmd – упрощенная версия гипервизора, без графического интерфейса. Программа похудела в два раза и при этом без проблем работает как OS/2 так и Windows. Правда редактировать данные, конечно же, нельзя, но это далеко не всегда и нужно. В Windows’е в случае если вы не настроили ассоциации файлов с расширением «cmd» с Regin’ой, потребуется сменить у данного файла расширение на «rexx».
  • Пример работы с RexxDB.rexx – демо программа с простыми запросами к базе данных. Не требует запуска гипервизора.
  • Процедуры для вставки в код.rexx – пакет процедур для вставки в код для работы с базой данных без гипервизора.
  • Процедуры для вставки в код 2.rexx – пакет процедур для вставки в код для работы с базой данных через гипервизор.
  • Примеры запросов – примеры запросов для тестирования работы облегченного гипервизора «СерверДанныхDOS.cmd».

 

В любой OS архив необходимо распаковать в корень диска «c:\» для сохранения прописанных путей. Впоследствии пути можно изменить.

Напрямую запустить приложения в OS/2 (eCS) невозможно, для этого их нежно запускать через программу «RexxDW.exe». У себя в DN/2 я прописал такой запуск по умолчанию для файлов типа «*.rexx». В архиве я положил одноименные файлы с расширением «*.cmd» для запуска программ под OS/2 (eCS) без создания нового типа расширения.