СПОСОБ РАЗРАБОТКИ, ХРАНЕНИЯ И ИСПОЛЬЗОВАНИЯ КОМПИЛИРОВАННЫХ В БИНАРНОЕ ПРЕДСТАВЛЕНИЕ ПРОГРАММ В ТАБЛИЦАХ БАЗ ДАННЫХ
METHOD OF DEVELOPMENT, STORAGE AND USE OF COMPILED TO THE BINARY REPRESENTATION OF PROGRAMS IN DATABASE TABLES

1 Область техники
Настоящее изобретение относится к области информационных технологий и конкретно к системам управления базами данных в части способов хранения и использования программ для обработки данных.
2 Уровень техники
В современных системах управления базами данных используется в основном библиотечный способ организации хранения и использования программ для обработки данных, занесенных в базу данных. Это способ применения хранимых процедур, которые регистрируются в системе под уникальными именами отдельно от данных и доступны для использования посредством указания этих имен. Дополнительно в системах управления базами данных применяются триггеры, которые предоставляют более гибкий способ вызова заключенных в них программ по событиям уровня таблицы данных, к которой триггер приписан. При этом использование триггеров в большей степени увеличивает сложность системы и вызывает дополнительные трудности в отладке из-за побочных эффектов. Оба этих метода можно назвать статичными, поскольку они не предполагают возможности модификации программ в процессе использования информационной системы, которая включает в себя базу данных.
В то же время программные модули так же являются информацией, как и данные, традиционно хранящиеся в базе данных. Можно ожидать, что возможности разработки и использования информационных систем возрастут, если программы обработки данных можно будет хранить в таблицах базы данных, наряду со всеми другими данными, как равноправный тип данных.
При этом для того, чтобы хранение и использование программ в качестве данных было эффективным, необходимо решить ряд проблем. В первую очередь, необходимо обеспечить способ хранения программ в откомпилированном бинарном виде для возможности эффективного использования программ. Во-вторых, необходимо реализовать способ обеспечения таких программ информацией из базы данных, которая необходима в качестве исходных данных. В-третьих, необходимо обеспечить механизм запуска программ и применения результатов в процессе эксплуатации информационной системы пользователем. В-четвертых, необходимо обеспечить способы обслуживания, связанные с анализом, отладкой и диагностикой программ. В-пятых необходимо обеспечить возможности контроля над побочными эффектами при использовании множества программ, хранящихся в таблицах наряду с другими данными, чтобы избежать непропорционально большого роста сложности информационной системы при ее разработке и развитии. В данном изобретении предлагаются способы решения этих задач.
3 Краткое описание изобретения
В данном изобретении предлагается способ разработки, хранения, использования и обслуживания программ, которые находятся в таблицах базы данных наряду с другими данными пользователя, и предназначены для выполнения расчетов на множестве данных базы данных. Указанные программы могут разрабатываться на языке высокого уровня, записываться и храниться в таблицах базы данных в бинарном откомпилированном виде, вызываться на исполнение из хранимых процедур, триггеров базы данных или программных приложений пользователя. Доступ к программам, как и другим данным, осуществляется по запросам базы данных.
Функциональность программ включает в себя методы извлечения исходных данных из базы данных и множество необходимых вычислительных операций для расчета результатов. Для обеспечения контроля над сложностью системы при использовании множества программных модулей, хранящихся в среде данных, во всех операциях, производимых в рассматриваемых программах, исключены побочные эффекты. То есть, все программы, разрабатываемые в рамках представляемого способа, являются программами-функциями. Это дает возможность для реализации программ использовать функциональный язык высокого уровня, который может обеспечить краткость при написании текстов программ и эффективность при их сопровождении.
Представление программ в таблицах базы данных является достаточным, также для их сопровождения. Описываемый способ обеспечивает возможность декомпиляции программ обратно в текстовую форму из бинарного представления. Кроме этого, в разработанных программах предусматривается верификация, диагностика и генерирование событий, которые могут использоваться системой управления базой данных для обработки ошибок и отката транзакций.
Эффект изобретения заключается в расширении возможностей разработки, настройки и применения информационных систем, базирующихся на использовании базы данных. Расширение возможностей основано на использовании функциональных программ обработки данных, которые хранятся в эффективном для использования бинарном представлении в таблицах данных наряду со всеми данными пользователя и могут эффективно разрабатываться, модифицироваться и использоваться в процессе эксплуатации информационной системы. Спектр использования таких функциональных программ может быть достаточно широк. В частности, они могут применяться для обновления данных в базе при изменении других данных. Функциональные программы-предикаты могут использоваться в качестве ограничений взаимосвязанных данных в базе данных. Также эти программы могут использоваться на уровне программных приложений, реализующих бизнес-логику информационной системы.
4 Раскрытие сущности изобретения
4.1 Введение
Способ разработки программ для хранения в бинарном виде в таблицах базы данных и использования их для вычислений на множестве данных базы данных основан на использовании программируемых пользовательских типов данных базы данных, создаваемых на объектно-ориентированном языке программирования.
В основе данного способа лежит разработка структуры классов, каждый из которых описывает множество функций функционального языка программирования для вычислений на множестве данных базы данных. Структура классов является иерархией, построенной на отношении наследования. Объекты классов играют роль конкретных функций.
Программные методы классов служат для обеспечения исполнения функций, описываемых классом, а также для их обслуживания, в частности, компиляции, записи в бинарное представление, обратной компиляции, диагностики.
Атрибуты классов являются параметрами, которые обеспечивают создание множеств различных функций из классов. С помощью структурных атрибутов формируются функции, имеющие в качестве результата структурные типы данных. В частности, с помощью структурных атрибутов формируются внутренние вызовы функций, на основе которых строится суперпозиция функций.
Текстовое представление программы функционального языка компилируется в структуру объектов классов, которая играет роль суперпозиции вызовов функций. В узлах структуры находятся внутренние вызовы функций, конечными элементами структуры являются вызовы функций без параметров. Корневым элементом структуры является вызов головной функции. Запуск программы функционального языка на исполнение осуществляется с помощью рекурсивного вызова специальных виртуальных методов классов, которые обеспечивают выполнение всей суперпозиции функций.
В последующих подразделах представлено детальное описание изобретения.
Объектно-ориентированный язык программирования, использующийся для реализации представленных в данном изобретении решений, в дальнейшем для краткости будет называться инструментальным языком. Функциональный язык программирования высокого уровня, который предназначается для разработки программ, хранящихся в таблицах базы данных, будет называться функциональным языком. Программа, разработанная на функциональном языке программирования, будет называться функциональной программой. Пользовательский тип данных базы данных, используемый для создания программ-функций, будет называться функциональным типом данных. Классы инструментального языка программирования, предназначенные для реализации функционального языка, будут называться классами функционального языка.
4.2 Структура классов для реализации функционального языка
Классы для реализации функционального языка представляют множества функций функционального языка программирования. Основные классы объединены в единую структуру отношением наследования. В структуре наследования каждый базовый класс описывает множество функций, которое является объединением множеств функций, представляемых наследуемыми классами. Два различных класса, имеющих общий базовый класс, представляют два множества функций, которые не имеют общих функций. В структуре наследования классов функционального языка находится один корневой базовый класс, который является объединением всех функций, представленных в функциональном языке. В целом структура классов функционального типа данных является деревом классов, построенном на отношении наследования, с единым корневым базовым классом.
Объект основного класса описывает конкретную функцию функционального языка программирования. Среди объектов есть сложные объекты, объекты, представляющие собой вызовы других функций, а также примитивные объекты, которые описывают функции без параметров. Часть классов являются абстрактными, они не предназначены для создания объектов. Абстрактные классы служат для описания множеств функций, то есть для представления типов функций.
Основные классы имеют атрибуты, множество различных значений которых определяет множество различных функций, представляемых классом. Структура наследуемых классов формируется таким образом, чтобы минимизировать количество атрибутов в классах, за счет вынесения однотипных атрибутов классов в их общий базовый класс.
Объект функционального типа данных является контейнером, содержащим объекты классов функционального языка. Структура объектов классов функционального языка внутри контейнера формирует суперпозицию функций, которая служит для выполнения вычислений на множестве данных базы данных.
4.3 Синтаксис функционального языка
Синтаксис языка программирования может быть представлен, как множество синтаксических правил. Способ компиляции программ функционального языка в рамках данной работы основан на том, что каждому основному классу сопоставлен свой набор синтаксических правил, который, таким образом, является синтаксисом этого класса. Поскольку классы взаимосвязаны, синтаксические правила одного класса могут ссылаться на синтаксические правила другого класса. При такой организации головное синтаксическое правило корневого класса в иерархии наследования является головным синтаксическим правилом всего функционального языка. При этом дерево классов, сформированное отношением наследования, играет роль дерева грамматического разбора для процесса компиляции из текстового представления программ функционального языка в объектную форму.
Синтаксическое правило для класса, который имеют производные классы, представляет собой набор альтернатив синтаксических правил производных классов, а также собственных синтаксических правил для своих объектов, принадлежащих исключительно данному классу. Если класс является абстрактным, соответственно, синтаксическое правило для своих объектов будет отсутствовать. Синтаксические правила класса для своих объектов основываются на описании синтаксиса атрибутов классов. Если в составе атрибутов присутствуют объекты других классов, синтаксические правила классов имеют ссылки на синтаксические правила других классов. В этом случае могут возникать рекурсивные ссылки.
Представленный способ формирования синтаксиса дает возможность расширять язык программирования без необходимости модифицировать уже разработанные программы и минимизировать объемы модификаций при изменении синтаксических правил для отдельных классов.
4.4 Компиляция программ функционального языка
Способ компиляции функционального языка основан на том, что в составе каждого класса должен присутствовать статический метод - функция на инструментальном языке, который выполняет компиляцию объектов этого класса. Этот метод получает на вход строку символов - текстовое определение класса, а на выходе создает объект класса, соответствующий текстовому определению. Поскольку синтаксические правила классов могут ссылаться друг на друга, компиляторы одних классов могут вызвать компиляторы других классов. Компилятор корневого класса в иерархии наследования классов является компилятором всего функционального языка.
Поскольку синтаксические правила функционального языка привязаны к реализующим его классам, сложность отдельной программы-компилятора в каждом классе будет достаточно низкой. При этом общая структура программ компиляции, является эффективной с точки зрения разработки, модификации и расширения функционального языка. Это обусловлено тем, что изменение существующего или добавление нового элемента функционального языка в основном вызовет необходимость изменения программы компиляции только в том классе, который связан с реализацией этого элемента.
Эффективность методов компиляции зависит от сложности языка. Поскольку речь идет о функциональном языке программирования, синтаксис языка может быть сформирован таким образом, чтобы синтаксический разбор выполнялся в основном программами обработки регулярных выражений, входящих в стандартные библиотеки многих языков программирования. Это дает возможность разработки компактных и эффективных методов компиляции.
Необходимо отметить, что расширение или модификация функционального языка производится в основном за счет элементов, которые являются конечными в дереве грамматического разбора. Поэтому компиляторы для новых или модифицируемых элементов языка, как правило, будут относительно простыми. Таким образом, расширение или модификация функционального языка в части изменения компилятора является несложной задачей.
4.5 Структура объектов функционального языка
В результате компиляции программы функционального языка генерируется структура объектов классов функционального языка. В узлах структуры находятся объекты, которые содержат в себе ссылки на другие объекты классов функционального языка. В частности, узлами служат объекты, которые являются вызовами функций и структурами данных. Если в функциональном языке предусмотрены другие структурные или ссылочные классы, такие как условные операторы, функции высшего порядка, идентификаторы, - то их объекты также будут играть роль узлов структуры объектов. При использовании таких объектов структура может быть рекурсивной. Построенная структура объектов имеет выделенную вершину, в которой находится головной вызов функции. В целом структура объектов однозначно отражает суперпозицию функций в программе, написанной на функциональном языке программирования.
4.6 Исполнение программ функционального языка
Каждый объект основного класса функционального языка играет роль функции. Вызов функции на исполнение осуществляется специальным виртуальным программным методом, который присутствует в каждом классе. Этот программный метод вычисляет результат функции, роль которой играет объект. Результатом функции является объект одного из классов функционального языка. В абстрактных классах данный метод может быть абстрактным. При вызове абстрактного метода в инструментальном языке программирования управление передается виртуальному методу объекта соответствующего типа. Программный метод вычисления результата функции для объекта, который является узловым в структуре объектов функциональной программы, в процессе выполнения вызывает аналогичные методы в связанных объектах. Таким образом, осуществляется выполнение запрограммированной суперпозиции функций. Расчет общего результата функциональной программы осуществляется вызовом данного программного метода в головном объекте структуры объектов.
Для каждого класса, который описывает функции запроса к базе данных, в представленном здесь способе, предусматривается хранимая процедура в базе данных, которая реализует действия оператора функционального языка и скрывает детали организации данных в базе. Программный метод, реализующий такой запрос в классе, является, таким образом, оболочкой вызова хранимой процедуры. При такой организации пользователь, разрабатывая программы на функциональном языке, оперирует с объектами высокого уровня, такими как множества, отношения, иерархия, а также операторами над этими объектами, которые находятся на уровне теоретико-множественных операций. При этом в функциональном языке нет необходимости учитывать особенности реализации структур данных в базе данных. Соответственно, изменения, производимые на уровне базы данных, не будут непосредственно влиять на уже разработанные программы функционального языка.
4.7 Декомпиляция программ функционального языка
Преобразование программ, написанных на функциональном языке, обратно в текстовую форму выполняется с помощью виртуального программного метода, который включен в каждый класс функционального языка. Этот метод переводит объект класса в текстовую форму в соответствии с синтаксическими правилами, установленными для этого класса. Для классов, которые описывают структурные или ссылочные объекты, программный метод декомпиляции может вызывать такой же метод других объектов. В абстрактных классах данный метод может быть абстрактным. При вызове метода управление будет передано в программный метод класса, соответствующий типу объекта, по правилам обработки виртуальных программ в объектно-ориентированном языке программирования. Вызов метода декомпиляции в головном объекте структуры объектов функционального языка обеспечивает получение текста исходной программы, написанной на функциональном языке.
При такой организации декомпиляции сложность разработки каждого отдельного метода перевода объектов обратно в текстовую форму будет достаточно низка. В целом расширение или модификация функционального языка в части разработки программных методов декомпиляции будет несложным, поскольку необходимые изменения в основном затронут только программный метод декомпиляции в модифицируемом или разрабатываемом классе.
4.8 Перевод в бинарное представление
Объекты пользовательского типа данных хранятся в таблице базы данных в бинарном представлении. Для перевода программы в бинарное представление и обратно используются специальные методы классов. Организация записи объектов в бинарное представление и извлечение их из этого представления аналогично организации декомпилятора и компилятора функционального языка.
Для записи в бинарное представление в каждом классе предусматривается виртуальный программный метод, который записывает объект в виде последовательности бинарных символов. Параметром метода является бинарный поток, в который ведется запись. При вызове данного метода исполняется программный метод класса, тип которого имеет объект, который переводится в бинарную форму. Для распознавания класса, к которому принадлежит объект в бинарном представлении, в качестве префикса объекта в бинарную запись включается дескриптор, однозначно указывающий на класс объекта. Для объектов, которые содержат другие объекты или ссылаются на них, данный метод вызывает одноименный программный метод для этих объектов. Вызов данного метода для головного объекта в структуре объектов функционального типа данных обеспечит перевод в бинарный вид всей функциональной программы.
Для считывания объектов из бинарного представления в объектную форму, в каждом классе предусматривается статический программный метод. Параметром метода является бинарный поток, из которого ведется считывание. Возвращаемым результатом метода является объект класса функционального языка, который генерируется на основе бинарной записи. Данный программный метод создает объект того класса, который обозначен дескриптором класса, записанного при переводе объекта в бинарную форму. Затем из бинарного потока считываются значения атрибутов. При этом данный метод может рекурсивно обращаться к аналогичным методам других классов. Вызов данного метода для корневого класса функционального языка обеспечивает полностью считывание объектного представления функционального программы из бинарного потока.
При такой организации каждый из методов записи и считывания будет достаточно простым. Модификация и расширение функционального языка в части разработки методов записи в бинарное представление и считывание из этого представления, также будет несложным, поскольку необходимые изменения локализуются в основном в самом изменяемом классе.
4.9 Формирование функционального типа данных в базе данных
Программы функционального языка формируются в базе данных в виде пользовательского типа данных, который разрабатывается на инструментальном объектно-ориентированном языке программирования. Пользовательский тип данных определяет программная структура (struct) инструментального языка, которая в данном способе играет роль оболочки для классов функционального языка программирования. Данная программная структура обеспечивает учет особенностей инструментальной среды для встраивания в нее пользовательского типа данных, обеспечивающего работу функционального языка программирования. В данной программной структуре в качестве атрибута включается головной объект структуры объектов функциональной программы. Дополнительно в качестве члена программной структуры оболочки присутствует элемент, указывающий на текущий для данной программы документ базы данных. Кроме этого в число членов включаются, объекты, необходимые для обслуживания функциональной программы, такие как флаг, указывающий на корректность программы и диагностические сообщения о возможных ошибках.
Использование пользовательского типа данных осуществляется с помощью программных методов данной программной структуры пользовательского типа данных. В числе методов присутствует программный метод, обеспечивающий вызов функциональной программы. Кроме этого определяются программные методы компиляции, декомпиляции, записи в бинарную форму и считывания из бинарной формы. Дополнительно включаются методы для обслуживания программы функционального языка, такие как проверка корректности, получение диагностики.
4.10 Использование программ функционального языка
Доступ к программам функционального языка осуществляется с помощью запросов к базе данных, также как выполняется доступ к остальным данным. Запись и чтение функциональных программ в базе данных происходит так же, как выполняется запись и чтение других данных. Вывод функциональных программ в интерфейсе пользователя осуществляется в их текстовом представлении.
Использование программ функционального языка осуществляется как на уровне базы данных в запросах к базе данных, в хранимых процедурах и триггерах; так и на уровне программных приложений пользователя. Разработка и модификация функциональных программ возможна в процессе эксплуатации и непосредственного использования информационной системы, в которой применяются функциональные типы данных.
5 Краткое описание чертежей
(tm)
На чертежах, включенных в описание изобретения, представлены диаграммы в нотации Universal Modeling Language - UML.
На Фиг. 1 изображен пример верхнего уровня модели классов программируемого типа данных для формирования программ на функциональном языке программирования.
На Фиг. 2 изображен пример модели классов, которые описывают метаданные базы данных для обеспечения доступа к базе данных из программ, разработанных на функциональном языке программирования.
На Фиг. 3 изображен пример модели классов, которые предназначены для создания структурных объектов функционального языка программирования, включая структуры данных и программные структуры.
На Фиг. 4 изображен пример модели классов, которые предназначены для работы с константами.
На Фиг. 5 изображен пример модели классов, которые являются предопределенными функциями, то есть операторами функционального языка программирования.
На Фиг. 6 изображен пример структуры объектов, соответствующей конкретной программе функционального языка программирования.
6 Осуществление изобретения
6.1 Введение
В данном разделе представлено осуществление решений, описанных в разделе 4, на примере одной конкретной функциональной программы. Структура данного раздела идентична структуре раздела 4. В каждом подразделе данного раздела рассматривается пример реализации, относящийся к способам, описанным в соответствующих подразделах раздела 4.
Примеры, описанные в данном разделе, носят иллюстративный характер. Конкретный вид функционального языка предметом данного изобретения не является. Данный способ предполагает возможность разработки множества различных языков для создания программ в таблицах базы данных, а также возможность их эффективного расширения и модификации.
(ТМ)
(ТМ)
Для описания примеров использовался язык программирования Microsoft Visual С#  и система управления базами данных Microsoft SQL Server. Для реализации представленных здесь решений, может быть использован произвольный объектно-ориентированном язык программирования и любой сервер базы данных, предусматривающий возможность создания пользовательских типов данных на этом языке.
6.2 Структура классов для реализации функционального языка
В данном подразделе описан пример структуры классов функционального языка программирования. Этот пример будет использован в дальнейших подразделах для построения варианта функционального языка и иллюстрации его использования на примере конкретной простой программы.
На Фиг. 1-5 представлен пример структуры классов функционального языка программирования. Каждый класс описывает множество функций функционального языка. Классы, которые описывают функции без параметров, являются, по сути, представлением множеств элементов, являющихся результатами этих функций. Соответственно, имена таких классов отражают названия множеств этих элементов.
На Фиг. 1 представлен верхний уровень структуры классов функционального языка. Тип данных 101 FunctionType представляет функциональный тип данных, определяемый в базе данных. Основные классы имеют единый корневой базовый класс 102 BaseObject, который обозначает общий для всех тип объектов инструментального языка. В функциональном языке программирования класс 102 описывает общий тип всех функций. Все объекты класса 102 BaseObject содержатся в объекте функционального типа данных 101 FunctionType и имеют доступ к нему по имени Function. Класс 103 BaseMetaData является производным классом от класса 102 и описывает множество функций, которые предоставляют мета данные базы данных. Мета данные базы данных используются в функциональном языке для построения запросов к базе данных. Класс 104 ObjectList является производным от класса 102 BaseObject и описывает множество функций, которые предоставляют структуры данных функционального языка, включая структуры данных и программные структуры. Класс 105 BaseConst является производным от класса 102 BaseObject и описывает константы, используемые в функциональном языке. Классы 102 BaseObject, 103 BaseMetaData, и 105 BaseConst являются абстрактными и не предназначены непосредственно для создания объектов.
На Фиг. 2 представлены классы, которые относятся к типу 103 BaseMetaData. Класс 201 Field представляет метаданные полей таблиц базы данных. В качестве атрибута в классе 201 Field используется наименование поля. Класс 203 Document представляет документы, которые формируются записями в таблицах данных. В данном примере один документ соответствует одной записи. Класс 203 Document имеет в качестве атрибута идентификационный номер, который в соответствующей таблице базы данных является ключевым. Класс 202 Property описывает объекты, являющиеся свойствами документа. Связь с классом 203 Document указывает, что свойство принадлежит определенному документу; при этом связь с классом 201 Field указывает, что свойство также принадлежит определенному полю таблицы. По сути это означает, что свойство определяется пересечением столбца и строки в таблице данных. Класс 202 Property имеет атрибут Property Value типа 105 BaseConst, который представляет значение данного свойства, то есть величину, которая находится в соответствующей строке и заданном поле таблицы базы данных.
При запросе к базе данных из функционального языка создается структура мета данных типа 103 BaseMetaData. Эта структура мета данных определяет множество данных в базе, необходимых для работы функциональной программы. Далее необходимые данные считываются из базы данных, и производится расчет результата.
На Фиг. 3 представлены классы, которые предназначены для создания структурных объектов, включая структуры данных и программные структуры. Класс 104 ObjectList содержит упорядоченное множество Tuple - кортеж произвольных элементов функционального языка, то есть элементов типа 102 BaseObject; а также целое число Count, в котором хранится количество элементов в данной структуре. Атрибут Tuple принадлежит классу List<BaseObject>, который имеет программный метод Add - для добавления элемента в список:
Доступ к элементам списка осуществляется по индексу - порядковому номеру элемента в списке:
Класс 301 FunctionCall является производным от класса 104 ObjectList и описывает объекты, являющиеся вызовом функции. Абстрактный класс 302 BaseOperator является производным от класса 104 ObjectList и объединяет множество классов, которые являются операторами - предопределенными функциями, выполняющими элементарные действия в функциональном языке.
Определение класса 104 ObjectList является рекурсивным и предполагает возможность построения вложенных структур произвольной сложности. При этом одни и те же элементы функционального языка могут агрегироваться в различные структуры типа 104 ObjectList.
Класс 301 FunctionCall представляет структурные объекты, состоящие из двух элементов: объекта типа 302 BaseOperator, который является исполняемым оператором, и базового объекта типа 104 ObjectList, который играет роль списка фактических параметров. В объекты класса 302 BaseOperator также включаются базовые объекты типа 104 ObjectList, которые в данном случае играют роль списка формальных параметров. Формальные параметры структуры 104 ObjectList, входящей в состав объекта типа 302 BaseOperator, заполняются фактическими параметрами структуры 104 ObjectList, входящей в состав объекта типа 301 FunctionCall, в процессе построения структуры объектов, которая представляет суперпозицию функций.
На Фиг. 4 представлены классы, которые предназначены для работы с константами. Классы 401 MoneyConst, 402 IntegerConst служат для создания объектов, которые представляют собой, соответственно, денежные единицы и целые числа. Необходимым атрибутом в этих классах является член класса Value, который хранит значение константы соответствующего типа. Атрибут Value в абстрактном классе 105 BaseConst имеет общий универсальный тип object. В классах 401 MoneyConst и 402 IntegerConst значение атрибута перегружается одноименным членом класса со специальным типом. В классы, представляющие константы, включен программный метод Addition - сложение. В классе 105 BaseConst этот метод является абстрактным:
В классах 401 MoneyConst и 402 IntegarConst этот метод является перегруженным и выполняет сложение, соответственно, десятичных и целых чисел. В качестве примера приводится текст метода Addition для класса MoneyConst:
В число реализуемых типов констант могут быть включены все типы данных, поддерживаемые базой данных, а также типы данных определяемые пользователем.
На Фиг. 5 представлены классы, которые являются примерами операторов функционального языка. Классы 501 CurrentDocument, 502 Sum, 503 PropertyList и 504 HierarchySubordinate являются производными от класса 304 BaseOperator. Объект класса 501 CurrentDocument является функцией, которая возвращает текущий документ, то есть документ, свойство которого вычисляет текущая функциональная программа. Объект класса 502 Sum является функцией, которая вычисляет сумму множества констант. Объект класса 503 PropertyList является оператором преобразования данных, который формирует множество свойств документов на основе множества документов и заданного поля таблицы. Объект класса 504 HierarchySubordinate является функцией доступа к базе данных, которая формирует множество дочерних документов, то есть документов, которые иерархически подчиненных заданному документу.
Приведенные здесь классы, реализующие операторы функционального языка, выбраны для описания примера, который будет рассмотрен далее в настоящем изложении. Набор операторов, который можно реализовать в рамках описываемого способа является модифицируемым и расширяемым.
6.3 Синтаксис функционального языка
Для описания примера синтаксиса функционального языка вводятся следующие обозначения. Если имя класса - Class, то множество текстовых представлений всех объектов класса обозначается: Class.Text. При этом текстовые представления собственных объектов класса без объектов производных классов, будет обозначаться как Class.Text.Base. Для абстрактного класса множество Class.Text.Base является пустым. Если класс не имеет производных классов то множества Class.Text и Class.Text.Base совпадают.
Для классов 102 BaseObject, 103 BaseMetaData, 104 ObjectList, 105 BaseConst, 302 BaseOperator, которые имеют производные классы, набор синтаксических правил, является следующим:
Для остальных классов, у которых нет классов производных, возможны различные варианты реализации текстового представления. Далее приводятся описания только тех синтаксических правил, которые будут использоваться в примерах программ:
В синтаксических правилах (6302) структурные объекты класса 104 ObjectList, представляются в виде списка объектов, заключенных в круглые скобки и разделенных запятыми; при этом список может быть пустым. Имена SchemaName, TableName и FieldName обозначают, соответственно, имена схемы базы данных, таблицы базы данных и поля в таблице. Текстовые строки 'CurrentDocument', 'Sum', 'PropertyList', 'HierarchySubordinate' являются предопределенными идентификаторами операторов функционального языка.
В качестве примера рассматривается программа функционального языка, вычисляющая сумму стоимостей задач, которые иерархически подчинены текущей задаче. В приведенном выше синтаксисе функционального языка (6301-6302) данная программа будет выглядеть следующим образом:
Головной структурой программы (6303) является вызов функции с оператором Sum. Результатом этого вызова будет сумма элементов списка, который является параметром функции. В свою очередь этот список вычисляется вызовом функции с оператором PropertyList. Эта функция рассчитывает список свойств для множества документов, при этом свойства соответствуют полю Management.Task.Cost, то есть определяются столбцом Cost в таблице Task, в схеме базы данных Management. Необходимый список документов, при этом, рассчитывается как множество документов, иерархически подчиненных текущему документу. Текущим документом при этом будет документ, возвращаемый функцией CurrentDocument().
6.4 Компиляция программ функционального языка
В данном подразделе рассматривается пример компилятора для функционального языка, построенного с помощью синтаксических правил (6301-6302). Для наглядности в приведенных примерах используются только стандартные функции инструментального языка, при этом вопросы оптимизации не рассматриваются. В качестве имени программы-компилятора класса принимается идентификатор Parse, компилятор собственных объектов класса будет называться BaseParse.
Для синтаксических правил (6301) компиляция может быть представлена как простой перебор альтернатив. Таким образом, например, для корневого класса 102 BaseObject, программа компиляции на инструментальном языке может быть записана следующим образом:
Если программа компиляции не может распознать входной текст, ее результатом будет пустая ссылка null, при этом в объекте функционального типа данных заносится сообщение об ошибке. Компиляторы для остальных классов, для которых синтаксис определен правилами (6301), устроены аналогичным образом.
Компиляция для структурных объектов типа 104 ObjectList реализуется с помощью цикла, в котором выполняется разбор последовательности объектов типа 102 BaseObject между разделителями текста программы. Созданные в процессе разбора объекты включаются в результирующий объект типа 104 ObjectList. Программа компиляции собственных объектов класса 104 ObjectList может быть записана следующим образом:
(tm)
Здесь методы StartsWith, EndsWith, Substring, IsNullOrEmpty являются стандартными функциями языка Visual С#. Функция StartsWith проверяет начало строки, EndsWith - проверяет конец строки, на совпадение с образцом. Функция Substring - вырезает подстроку, в данном случае освобождает входной текст от внешних скобок. Функция IsNullOrEmpty проверяет, является ли строка пустой. Метод GetFirstObjectText играет роль лексического анализатора. Этот метод вырезает из входной строки запись первого объекта в списке и возвращает во втором параметре оставшуюся строку входного текста программы. Текст этого метода является очевидным и здесь не приводится.
Компиляция для вызовов функций, то есть объектов типа 301 FunctionCall выполняется аналогичным образом. В соответствии с синтаксическим определением (6302) сначала компилируется оператор вызова программы, а затем список параметров. В случае успеха из полученных объектов создается новый вызов функции - объект типа 301 FunctionCall. Программа компиляции для этих объектов может быть записана следующим образом:
Программы компиляции для остальных синтаксических правил (6302) являются более простыми и формируются аналогичным образом.
6.5 Структура объектов функционального типа данных
В результате компиляции программы (6303) программой-компилятором (6401) будет сгенерирована структура объектов, приведенная на Фиг. 6. В данном случае структура представляет собой иерархическое дерево объектов, принадлежащих классам, представленным на Фиг. 1-5. Связи между объектами являются экземплярами ассоциаций между классами, которые показаны на Фиг. 3.
В корне дерева находится объект 601, который принадлежит классу 301 FunctionCall. Это головной вызов функции. В объект 601 включен объект 602, который является экземпляром класса 502 Sum, и объект 603 класса 301 FunctionCall, который является единственным параметром головной функции. Объект 603 является вызовом функции с оператором, которым является объект 604 класса 503 PropertyList, и двумя параметрами. Первый параметр - объект 605, являющийся вызовом функции, а второй - объект 606 класса 201 Field. Вызов функции 605 содержит оператор - объект 607 класса 504 HierarchySubordinate и параметр - объект 608, который является последним в данной структуре вызовом функции. Вызов функции 608 содержит оператор - объект 609 класса 501 CurrentDocument, для которого параметры не предусмотрены. Таким образом, структура объектов на Фиг. 6 отражает суперпозицию вызовов функций программы (6303).
При отображении объектов на Фиг. 6 указаны конкретные значения их атрибутов. В объектах класса 301 FunctionCall устанавливается используемый оператор. Значение Count в объектах типа 304 BaseOperator означает количество формальных параметров, в объектах типа 301 FunctionCall - количество фактических параметров для вызовов функций. Количество фактических и формальных параметров должно совпадать для вызова функции и применяемого в ней оператора. При построении структуры объектов это служит примером проверки корректности исходной программы в методе (6403).
6.6 Исполнение программ функционального языка
В настоящем подразделе рассматриваются примеры программных методов вызова функций для классов, представленных на Фиг. 1-5. В качестве имени метода будет использоваться InternalCall.
Для корневого абстрактного класса 102 BaseObject метод InternalCall является абстрактным. Для классов 103 BaseMetaData, 104 ObjectList, 105 BaseConst метод InternalCall является тривиальным: он возвращает в качестве результата сам объект. В классе 204 Property определение метода InternalCall должно быть перегружено. Результатом этого метода будет константа, являющаяся значением объекта класса 204 Property:
Для операторов функционального языка, то есть объектов класса 302 BaseOperator метод InternalCall рассчитывает результат, который является результатом конкретной функции, реализованной в классе.
Для класса 502 Sum, объекты которого играют роль математической функции, рассчитывающей сумму элементов множества, метод InternalCall может выглядеть следующим образом:
Оператор Sum имеет один параметр. Конструкция Tuple[0] извлекает этот параметр из базового объекта типа 104 ObjectList и присваивает его переменной const list, которая таким образом будет содержать заданное множество констант. Далее выполняется цикл с вызовом функции сложения Addition. Причем в зависимости от типа констант, будет вызываться функция сложения класса 401 MoneyConst или 402 IntegerConst. Результатом метода InternalCall будет объект, представляющий сумму констант заданного множества соответствующего типа. Если множество констант пусто, результатом функции будет, в данном случае, нулевое значение константы типа 401 MoneyConst.
Для класса 501 CurrentDocument, объекты которого играют роль оператора, возвращающего текущий документ, метод InternalCall записывается следующим образом:
Данный метод возвращает объект, являющийся текущим документом, который находится в объекте функционального типа данных базы данных. Информация о текущем документе заносится в объект функционального типа данных при внешнем вызове функциональной программы (раздел 5.9).
Для объектов-операторов, которые обращаются с запросами к базе данных, метод InternalCall является оболочкой вызова хранимой процедуры базы данных из среды инструментального языка. Среди классов-операторов, показанных на Фиг. 5, два класса определяют операторы, обращающиеся к базе данных: это класс 503 PropertyList и класс 504 HierarchySubordinate. Методы вызова запрограммированной в классах функции реализуются в них одинаковым образом. В качестве примера рассматривается метод InternalCall для оператора 504 HierarchySubordinate:
(tm)
Используемые в этой программе классы SqlConnection, SqlCommand, SqlDataReader являются стандартными классами в библиотеке Visual C#. В приведенном методе сначала устанавливается соединение с базой данных conn, затем формируется команда базы данных cmd. Далее указывается имя команды Management.HierarchySubordinates, тип команды как хранимой процедуры. После чего создается параметр хранимой процедуры с именем @Id, типом Int32, направлением использования - ввод, и величиной, которая равна ключевому номеру документа из первого параметра в списке параметров оператора. Сформированный параметр добавляется в список параметров хранимой процедуры, после чего команда запускается на исполнение. Результатом процедуры Management.HierarchySubordinates будет поток данных, представляющий таблицу с единственным столбцом, содержащим ключевые номера документов, которые иерархически подчинены данному документу. В заключении, в методе InternalCall полученные номера последовательно в цикле считываются из потока, и формируется объект типа 104 ObjectList, состоящий из документов с полученными ключевыми номерами.
(tm)
Вызываемая в данном методе хранимая процедура базы данных ManagementHierarchySubordinates может иметь следующий вид на языке SQL:
В данной хранимой процедуре производится выборка ключевых номеров тех задач, для которых ключевой номер родительской задачи равен заданному номеру.
При этом таблица Task, к которой обращается программа, в минимальном определении записывается следующим образом:
В таблице (6606) иерархия документов организована с помощью ссылок на родительский документ: CONSTRAINT [FK_Task]. Метод, с помощью которого организована иерархия документов в данном примере, не влияет на построение функциональной программы. Особенности метода учитываются только в хранимой процедуре, которая выполняет обработку иерархических связей.
В определении таблицы также продемонстрировано хранение функциональных программ в базе данных. В данном случае функциональные программы хранятся под пользовательским типом данных [Management].[FunctionType], который в инструментальном языке определяется типом данных 101 FunctionType.
Центральным узлом для организации суперпозиции функций является метод расчета результата функции для класса 301 FunctionCall:
В данном примере сначала вычисляются и заполняются параметры оператора, после чего оператор вызывается на исполнение. Если в числе параметров объекта 301 FunctionCall есть другие вызовы функций, то они будут обрабатываться рекурсивно до конечных объектов, для которых метод InternalCall возвращает сам объект. Таким образом, рассчитывается суперпозиция функций произвольной глубины.
6.7 Декомпиляция программ функционального языка
(tm)
В данном подразделе приведены варианты основных программных методов декомпиляции для классов, представленных на Фиг. 1-5. Имя программного метода, переводящего объект в текстовую форму - ToString. Одноименный метод является стандартным в инструментальном языке Visual C# для получения текстового представления объектов.
Метод ToString для абстрактного класса 102 BaseObject является абстрактным. Для структурных объектов метод ToString вызывает одноименный метод для вложенных объектов. Например, для класса 301 FunctionCall данный метод может быть записан следующим образом:
Здесь выполняется конкатенация двух строк: записи оператора и текстового представления базового объекта типа 104 ObjectList, который играет роль списка параметров для данного функционального вызова.
Для класса 104 ObjectList метод ToString может быть записан следующим образом:
В результате работы данной программы будет получен список из текстовых представлений объектов, заключенных в объекте 104 ObjectList, разделенных запятыми, и заключенный в круглые скобки.
Методы декомпиляции для остальных классов являются более простыми, по сути, они выводят в качестве результата текстовое представление своих атрибутов.
В целом, при вызове метода ToString для головного объекта, представленного на Фиг. 6, будет получена строка (6303). То есть пользователь, работающий с системой, всегда может видеть актуальное текстовое представление программ, хранящихся в таблицах базы данных.
6.8 Перевод в бинарное представление
(tm)
В приводимых ниже примерах в качестве дескриптора, указывающего на класс записываемого объекта, используется наименование класса. Имена программных методов записи и считывания в примерах являются стандартными для аналогичных программ в среде инструментального языка Visual C#: это Write и Read.
Метод Write каждого класса, прежде всего, вызывает этот же метод для базового класса, затем записывает дескриптор класса и после этого атрибуты класса. Для корневого класса 102 BaseObject метод Write может быть записан следующим образом:
Для абстрактного класса 105 BaseConst метод Write обращается к такому же методу базового класса:
Для класса 104 ObjectList метод Write выполняет цикл по объектам списка:
Здесь после вызова метода Write базового класса, записывается величина, указывающая на количество элементов в структуре, далее в цикле последовательно записываются все элементы структуры.
Для класса 301 FunctionCall метод Write является достаточно простым:
В данном методе вначале выполняется метод Write базового класса 104 ObjectList, в рамках которого записывается список параметров. Затем записывается оператор вызова функции.
Для остальных классов метод Write является еще более простым и записывается аналогичным образом.
Статический метод Read для корневого класса 102 BaseObject считывает дескриптор класса и, используя его как переключатель, вызывает аналогичный метод соответствующего класса:
В данном методе осуществляется выбор класса, объект которого записан в бинарном потоке, и вызывается программный метод чтения объекта этого класса.
Метод Read для класса 104 ObjectList выполняет цикл по объектам списка:
Здесь сначала считывается количество элементов в структуре, затем организуется цикл, в котором последовательно считываются все элементы списка.
Метод Read для класса 301 FunctionCall последовательно считывает список параметров и оператор:
Методы считывания и записи в бинарное представление для остальных классов формируются аналогичным образом.
6.9 Формирование функционального типа данных в базе данных
В данном подразделе приводится пример программной структуры пользовательского типа данных, служащей оболочкой для функциональных программ, основанных на классах, представленных на Фиг. 1-5.
Заголовок программной структуры пользовательского типа данных формируется следующим образом:
В данном определении указывается атрибут программной структуры инструментального языка (SqlUserDefinedType), указывающий на пользовательский тип данных (Format.UserDefined). Наименование структуры - Function. Структура имеет интерфейсы, один из которых позволяет принимать в базе данных неопределенные значения (INullable), второй - записываться в бинарное представление (IBinarySerialize). В качестве членов структуры определены сама функциональная программа, то есть ее головной объект (FunctionProgram), текущий документ и строка диагностики, для вывода ее пользователю. В числе методов присутствует метод Call для вызова функциональной программы на исполнение. Параметром метода Call является ключевой номер, идентифицирующий текущий документ. Дополнительно определены метод проверки корректности (IsValid), функция получения диагностики (GetErrorMessage), программный метод компиляции (Parse), функция обратной компиляции (ToString), методы записи в бинарную форму и считывания из бинарной формы (Read и Write). Атрибуты программных методов в инструментальном языке (SqlMethod и SqlFunction) обеспечивают возможность вызова этих методов из среды сервера базы данных.
Метод вызова функциональной программы Call записывается следующим образом:
Здесь сначала устанавливается текущий документ в атрибуте структуры CurrentDocument. Это значение используется в дальнейшем в операторе функционального языка 501 CurrentDocument. Затем вызывается метод InternalCall для головного объекта программы функционального языка (FunctionProgram). Результатом метода является значение константы общего типа object. При вызове из среды сервера базы данных будет получена константа в универсальном типе sql_variant.
Остальные методы структуры пользовательского типа данных являются простыми: по сути, они представляют оболочки для вызова соответствующих методов головного объекта функциональной программы FunctionProgram. В качестве примера, приводятся определения программных методов компилятора и декомпилятора структуры пользовательского типа данных:
Программные методы для чтения программной структуры функционального типа данных из бинарного представления и записи в бинарное представление формулируются следующим образом:
(tm)
Методы компиляции и преобразования в бинарное представление вызываются в процедурах базы данных автоматически при преобразовании типов данных и записи объектов в таблицы базы данных. Например, в следующем фрагменте кода на языке SQL декларируется переменная с функциональным типом данных и производится компиляция программы на функциональном языке с присвоением результата этой переменной. Затем, если компиляция прошла успешно, в таблицу Task заносятся функциональные программы. В противном случае выводится диагностика:
В результате выполнения этого фрагмента в таблицу Task будет занесена откомпилированная бинарная форма программы (6303). В данном примере во все строки таблицы заносится одна и та же функция. В реальности возможны разные варианты использования различных алгоритмов для различных типов задач, создания связанных таблиц для хранения программ обработки и. т.п.
Внутренний программный метод Error используется внутри классов функционального языка для записи сообщений об ошибках. Внешний метод - функция Is Valid проверяет наличие сообщений об ошибках и возвращает соответствующий флаг в вызывающую процедуру.
Внутри метода обработки ошибок могут также генерироваться события, которые будут передаваться на обработку на уровне сервера базы данных или бизнес-приложения.
Структуру пользовательского типа данных для функционального языка программирования можно назвать стандартной: она практически не зависит от вида функционального языка. При этом необходимо отметить, что в одной базе данных может использоваться произвольное количество функциональных типов данных, реализующих свои функциональные языки программирования.
6.10 Использование программ функционального языка
В этом подразделе приводятся примеры использования функциональной программы в базе данных. В первом примере представлен триггер для таблицы базы данных (6606) Management.Task, который пересчитывает значение стоимости задач, для которых изменилась функция, рассчитывающая эту стоимость:
Триггер (6101) выполняет обновления, если модификации подвергается данные из столбца Function таблицы (6606), то есть изменяются функциональные программы пересчета. Пересчет осуществляется только для тех задач, в которых изменились функциональные программы. Запуск функциональных программ осуществляется вызовом метода Call функционального типа данных. В качестве параметра при вызове передается ключевой номер документа - задачи. Полученная в результате вызова константа преобразуется к денежному типу и заносится в поле стоимости задач.
Следующий пример аналогичен первому, но в нем осуществляется каскадное обновление данных по всей иерархии задач.
Данный триггер выполнит изменения, если модификации подверглись значения стоимостей задач. В триггере будут пересчитываться значения стоимостей только для родительских по иерархии задач, для которых изменены подчиненные - дочерние задачи. При изменении стоимости родительских задач, данный триггер сработает вновь. Рекурсия будет продолжаться до тех пор, пока все значения стоимостей в зависимых задачах не будут обновлены.
Аналогично функциональные программы могут вызываться из хранимых процедур, которые запускаются автоматически по событиям или по инициативе пользователя. Естественно вызов функциональных программ, которые реализуются на инструментальных языках программирования, возможен также из программных приложений пользователя.