SQL-программы, использующие язык программирования JAVA
ОТ ПЕРЕВОДЧИКА
Стремление использовать популярный язык Java в разработках приложений систем баз данных, потребовало создания необходимого для этих целей инструментария. Первым шагом в этой области стала, как известно, разработка компанией Javasoft спецификации JDBC интерфейса прикладного программирования на языке Java для доступа к реляционным базам данных, включенной компанией в состав JDK 1.1. Средства JDBC обеспечивают динамическое связывание Java-программ с SQL-базами данных. Создание дополнительных возможностей более высокого уровня для взаимодействия языка Java с системами реляционных баз данных было целью образованного в апреле 1997 года консорциума в составе компаний Compaq, IBM, Micro Focus, Microsoft, Compaq, Informix, Oracle, Sun и Sybase. В короткие сроки его участники разработали спецификации SQLJ, состоящие из трех частей, и предложили их ANSI для принятия в качестве официального стандарта. SQLJ Часть 0 обеспечивает встраивание статических операторов SQL в Java-программы и тем самым доступ из них к базам данных. Эта спецификация вошла как одна из частей в разрабатываемый новый стандарт языка SQL под названием «Связывания для объектных языков (SQL/OLB)». Она была одобрена ANSI в декабре 1998 года В сентябре 1999 года аккредитованный при ANSI Национальный комитет по стандартам информационных технологий (NCITS) одобрил SQLJ Часть 1 - спецификацию, которая дает возможность создавать хранимые Java-программы для SQL-баз данных. Третья часть проекта - SQLJ Часть 2 «Типы данных SQL, использующие язык программирования Java» находится в настоящее время на рассмотрении в ANSI. Ранее в нашем журнале (см. N 4 за 1999 года) был опубликован перевод статьи, посвященной SQLJ Части 0, редакторов колонки стандартов из журнала SIGMOD Record Э. Айзенберга и Дж. Мелтона. Ниже мы публикуем перевод следующей статьи этих авторов, посвященной SQLJ, в которой обсуждаются возможности только что одобренного стандарта SQLJ Части 1.
М. КогаловскийВВЕДЕНИЕ
Неформальная группа компаний SQLJ продолжала продуктивно работать с тех пор, как мы в последний раз писали о них в декабре 1998 года [1]. В то время мы обсуждали стандарт SQLJ Часть 0 [2], который только что был одобрен как стандарт NCITS «SQL Часть 10: Связывания для объектных языков» (SQL Part 10: Object-Language Bindings, SQL/OLB). Этот стандарт позволяет встраивать операторы SQL в программы на языке Java.
В первой половине 1999 года группа SQLJ, состоящая из компаний Cloudscape, Compaq (Tandem), IBM, Informix, Oracle, Sun и Sybase, завершила работу над спецификацией SQLJ Часть 1 и запросила NCITS принять ее по ускоренной процедуре.
В сентябре 1999 года это произошло - SQLJ Часть 1 была принята в качестве стандарта NCITS 331.1-1999 [3], и теперь документацию этого стандарта можно приобрести через NCITS. Заслуживает внимания то обстоятельство, что эта спецификация чрезвычайно доступна, включает большой учебный материал, который служит введением к ее более нормативным элементам.
Компания Sybase представила спецификацию SQLJ Часть 1 группе SQLJ в начале 1997 года Фил Шоу (Phil Shaw) из Sybase выполнял функции редактора этого документа на протяжении всей его разработки. SQLJ Часть 1 позволяет включать в среду СУБД классы Java, содержащиеся в Jar-файлах. Методы этих классов могут быть далее использованы в качестве реализации хранимых процедур и хранимых функций SQL (называемых вместе хранимыми программами).
В этой работе приводится сначала краткое введение в SQL-программы (SQL Routines), а затем мы перейдем к рассмотрению возможностей SQLJ Части 1.
SQL-ПРОГРАММЫ
В 1996 г. в «SQL Части 4: Долговременно хранимые модули (SQL Persistent Stored Modules», SQL/PSM) [4] была предложена концепция SQL-программ. Мы обсудим ее в этом разделе, главным образом, на примере.
SQL-функции имеют только входные параметры и продуцируют результат некоторого определяемого SQL типа данных. После того, как функция определена, она может вызываться из любого выражения.
CREATE FUNCTION pub_year (INTEGER volume) RETURNS CHAR (4) BEGIN RETURN CAST (volume + 1971 AS CHAR(4)); END ; SELECT * FROM sigmod_articles sa WHERE pub_year (sa.volume) = ?1995?;
В этом примере функция pub_year определена таким образом, что она имеет в качестве аргумента целое и возвращает строку длиной в четыре литеры. Эта функция используется далее в предложении WHERE запроса.
SQL-процедуры могут иметь параметры, для которых можно задать режим IN, OUT или INOUT (IN - значение по умолчанию). Они не возвращают значений таким образом, как это делают SQL-функции.
CREATE PROCEDURE pub_info ( IN INTEGER volume, IN INTEGER no, OUT CHAR(4) yyyy, OUT CHAR(3) mmm) BEGIN yyyy = pub_year (volume); CASE no WHEN 1 THEN SET mmm = ?MAR?; WHEN 2 THEN SET mmm - ?JUN?; WHEN 3 THEN SET mmm = ?SEP?; WHEN 4 THEN SET mmm = ?DEC?; ELSE SET mmm = ?????; END CASE ; END DECLARE p_year CHAR (4); DECLARE p_month CHAR (3); CALL pub_info (22, 3, p_year, p_month);
В этом примере определяется процедура pub_info, которая принимает в качестве входных параметров том и номер журнала, а затем производит результирующие значения - год и месяц его выпуска, которые соответствуют заданным входным параметрам. После того, как вызвана pub_info, переменные p_year и p_month будут содержать, соответственно, ?1993? и ?SEP?. В обоих приведенных типах SQL-программ тело состоит из оператора SQL, который может быть и составным (BEGIN/END).
Оба типа программ также могут быть определены как внешние программы. Такие программы имеют ту же самую сигнатуру, но вместо тела с операторами SQL они содержат имя точки входа в код, написанный на некотором языке третьего поколения (3GL).
CREATE FUNCTION pub_year (INTEGER volume) RETURNS CHAR (4) LANGUAGE c EXTERNAL NAME ?pubyear@sigmod.dll? PARAMETER STYLE GENERAL
Формат строки, которая идентифицирует внешнюю точку входа, определяется конкретной СУБД (иногда на него оказывает влияние конкретная операционная система). Вызов этих внешних программ ничем не отличается от SQL-программ.
Отметим, наконец, что процедуры обладают еще одной возможностью, о которой следует упомянуть. Они могут возвращать один или более наборов результатов наряду с теми скалярными значениями, которые они возвращают через параметры OUT и INOUT. Интерфейсы уровня вызовов, такие как ODBC и JDBC, предусматривают некоторые методы, которые позволяют приложению исследовать эти наборы результатов.
CREATE PROCEDURE article_count (IN INTEGER a_volume) READS SQL DATA DYNAMIC RESULT SETS 1 BEGIN DECLARE result CURSOR WITH RETURN FOR SELECT no, COUNT(*) FROM sigmod_articles sa WHERE sa.volume = a_volume GROUP BY no ; OPEN c1; END ;
В приведенном примере сначала устанавливается, что он будет возвращать не более одного результирующего набора. Далее в этой процедуре объявляется и открывается курсор, который будет возвращать программе, вызвавшей процедуру, строки результата, поскольку этот курсор был декларирован с атрибутами WITH RETURN и поскольку он был оставлен открытым.
Теперь, после того, как мы вспомнили основные возможности SQL/PSM, можно перейти к новому материалу. Дополнительную информацию относительно SQL-программ читатель может найти в ранней статье Э. Айзенберга [5].
УСТАНОВКА JAR-ФАЙЛОВ
Первый шаг, который вы должны сделать для использования SQLJ Часть 1, состоит в том, чтобы импортировать классы Java в базу данных. Ваши классы должны быть в одном или более
Jar-файлов. Это довольно легко сделать, используя утилиту Jar, которую поставляет компания Sun в ее инструментальных пакетах Java SDK.
Пусть, например, мы имеем класс Java, содержащий реализацию функции SQL pub_year, которую мы видели ранее.
public class Sigmod { public static String pubYear (int volume) { return String.valueOf (volume + 1971); } }
Предположим далее, что этот класс был скомпилирован и помещен в файл Sigmod.jar. Мы могли бы установить этот Jar-файл в базу данных с помощью следующего оператора:
CALL sqlj.install_jar (?file:///d:/Sigmod.jar?, ?sigmod_jar?, 0);
Эта операция будет копировать содержимое Jar-файла (указанного с помощью URL) в базу данных и присвоит SQL-имя этому Jar-файлу. Первые две части SQL-имени этого Jar-файла (имя каталога и имя схемы) не указываются в этом примере, и поэтому они являются определенными неявно (в SQL для этого предусматриваются правила, которые применяются в таком случае к именам многих видов объектов базы данных). Хотя приведенный оператор имеет форму вызова процедуры, на самом деле он действует как оператор языка определения данных (DDL). Последний его аргумент 0 указывает, что не должен использоваться дескриптор развертывания (мы обсудим дескрипторы развертывания немного позднее).
Для краткости мы ограничимся здесь только упоминанием того, что SQLJ Часть 1 предоставляет процедуру sqlj.replace_jar и процедуру sqlj.remove_jar, которые позволяют поддерживать установленные Jar-файлы и осуществлять в них необходимые изменения с течением времени.
СОЗДАНИЕ JAVA-ПРОГРАММ
Для того, чтобы содержимое Jar-файла могло принести пользу, нужно иметь виртуальную машину Java (Java Virtual Machine, JVM). Поставщик СУБД должен предоставлять специализированную JVM вместе с его программным продуктом либо он может предусмотреть возможность применения любой JVM, которая поддерживается используемой операционной системой.
Классы, которые мы импортировали, могут содержать множество методов. В SQLJ Части 1 мы будем непосредственно использовать только такие методы, которые одновременно обладают атрибутами public и static. Статические методы выполняются независимо от какого-либо конкретного экземпляра объекта. Подобно всем другим методам, эти методы могут возвращать значения некоторого типа данных, или они могут быть снабжены атрибутом void.
Итак, мы располагаем общедоступными статическими (public static) методами, JVM, в которых они могут выполняться, и желаем вызывать их изнутри SQL. Одно из решений, которое можно было бы принять, состоит в том, чтобы ввести новые операторы SQL для вызова этих методов. Вместо этого, однако, было принято иное решение, предусматривающее обеспечение отображения SQL-программ в методы Java. При таком подходе приложение может вызывать эти программы точно таким же образом, как оно вызывало бы другие типы SQL-программ. Java-программа (Java Routine) является такой программой, которая имеет отображение в реализующий ее метод Java.
Отображаемые типы данных
Для вызова SQL-программы, который бы приводил к вызову метода Java, аргументы этой SQL-программы должны стать аргументами метода Java. При этом имеет смысл использовать в SQLJ Части 1 то же самое отображение, что и определено в JDBC между типами данных SQL и типами данных Java.
Напомним, что Java предоставляет некоторые примитивные типы данных, которые имеют адапторы (wrappers) ассоциированных с ними классов. Тип данных SQL и тип данных Java являются отображаемыми, если они являются отображаемыми простым образом (simply mappable) - в примитивные типы данных Java, объектно отображаемыми (object mappable) или отображаемыми по выходу (output mappable). Этот последний вариант мы обсудим, когда перейдем к рассмотрению процедур Java.
В этой таблице «-» указывает то же самое значение, что и указано в столбце «Объектно отображаемый».
Функции Java
В SQLJ Части 1 определен новый вариант CREATE FUNCTION, который обеспечивает отображение между сигнатурой функции SQL и методом Java.
Ссылка SQL на конкретный метод Java состоит из трех частей:jar-имя : имя метода (сигнатура).
Здесь «jar-имя» - это имя SQL, которое было дано Jar-файлу, «имя метода» состоит из имени метода вместе с указанием класса и, возможно, пакета, который его содержит. Третья часть ссылки «сигнатура» - является факультативной. Если она не задана, то используется сигнатура, содержащая тип данных Java по умолчанию для каждого типа данных SQL в сигнатуре SQL.
Определим функцию Java следующим способом:
CREATE FUNCTION j_pub_year (INTEGER volume) RETURNS CHAR (4) EXTERNAL NAME ?sigmod_jar:pubYear? LANGUAGE JAVA PARAMETER STYLE JAVA ;
Чтобы создать функцию Java, параметры SQL должны быть отображаемыми в соответствующие параметры метода Java, типы результатов их обоих должны быть отображаемыми, и метод должен быть одновременно как public, так и static (как мы уже упоминали ранее).
После того, как функция Java создана, она вызывается точно так же, как любая другая функция SQL.
SELECT * FROM sigmod_articles sa WHERE j_pub_year (sa.volume) = ?1995?;
Процедуры Java
Отображение параметров, которое обсуждалось в предыдущем разделе при рассмотрении функций Java, должно быть расширено для случая параметров вида INOUT и OUT, которые может содержать процедура SQL. Это представляет собой некоторую проблему, поскольку Java непосредственно не предоставляет какого-либо способа, позволяющего методу возвращать более одного значения в качестве результата его выполнения.
Тип данных SQL | Отображаемый простым образом | Объектно отображаемый |
CHAR | - | String |
VARCHAR | - | String |
LONGVARCHAR | - | String |
NUMERIC | - | java.math.BigDecimal |
DECIMAL | - | java.math.BigDecimal |
BIT | boolean | Boolean |
TINYINT | byte | Integer |
SNALLINT | short | Integer |
INTEGER | int | Integer |
BIGINT | long | Long |
REAL | float | Float |
FLOAT | double | Double |
DOUBLE | double | Double |
BINARY | - | byte[] |
VARBINARY | - | byte[] |
LONGVARBINARY | - | byte[] |
DATE | - | java.sql.Date |
TIME | - | java.sql.Time |
TIMESTAMP | - | java.sql.Timestamp |
Однако Java позволяет изменять состояния объектов, которые передаются методу. В SQLJ Части 1 принято соглашение о том, что метод Java должен использовать одноэлементный массив для передачи значений параметров OUT и INOUT. Единственный элемент этого массива может быть прочитан методом как входное значение, и метод может установить для него выходное значение. Это приводит нас к третьей форме отображения, упоминавшейся ранее. Тип данных SQL является отображаемым по выходу в массив типов Java, в который он отображаем простым образом или объектно-отображаем. Это означает, что тип SQL INTEGER является отображаемым по выходу в int[] или в Integer[].
Предположим, что следующий метод, обеспечивающий реализацию в Java процедуры SQL pub_info, которую мы рассматривали ранее, был включен в наш класс Sigmod:
public static void pubInfo (int volume,int no, String[] yyyy, String[] mmm) { yyyy[0] = pubYear(volume); if (no == 1) mmm[0] = «MAR»; else if (no == 2) mmm[0] = «JUN»; else if (no == 3) mmm[0] = «SEP»; else if (no == 4) mmm[0) = «DEC»; else mmm[0] = «???»; }
Этот метод может использоваться для определения процедуры Java следующим образом:
CREATE PROCEDURE j_pub_info (IN INTEGER volume, IN INTEGER no, OUT CHAR(4) yyyy, OUT CHAR(3) mmm) EXTERNAL NAME ?sigmod_jar:pubInfo (int, int, String[],String[])? LANGUAGE JAVA PARAMETER STYLE JAVA ;
Чтобы создать процедуру Java, параметры IN процедуры SQL должны быть отображаемыми в параметры соответствующего метода, параметры процедуры SQL INOUT и OUT должны быть отображаемыми по выходу в параметры соответствующего метода, и этот метод должен быть объявлен как void.
Массивы Java невидимы для операторов SQL CALL. SQL автоматически создает и заполняет массив Java, когда имеет место вызов, и он осуществляет выборку необходимых элементов, когда завершится вызов.
И вновь мы видим, что вызов процедуры Java никак не отличается от вызова процедуры SQL.
CALL j_pub_info (22, 3, p_year, p_month);
Обработка исключительных ситуаций
В методе Java, который положен в основу Java-программы, исключительные ситуации могут возникать и перехватываться обычным образом. Если исключительная ситуация не перехвачена методом Java, то возникнет исключительная ситуация SQL, порожденная оператором SQL, который вызвал Java-программу.
Доступ к базе данных из метода Java
Давайте теперь рассмотрим среду, в которой происходят все эти вызовы SQL и Java. Оператор SQL выполняется для некоторого пользователя, возможно на клиентской машине (с помощью вызова ODBC или вызова метода JDBC). Оператор SQL порождает вызов метода Java в JVM. Для некоторых методов может потребоваться обращение к базе данных как часть процесса их исполнения. Для того, чтобы иметь возможность это делать, они должны были бы содержать вызовы методов JDBC.
Такие операторы JDBC могли бы пытаться соединиться с некоторой конкретной базой данных, специфицируя ее URL в методе getConnect. СУБД может допустить или не допустить установление такого соединения. Однако СУБД должна позволять Java-программе соединяться с базой данных в случае, когда специфицирован специальный URL jdbc:default:connection. Такое соединение дает возможность методу Java разделять сеанс и контекст транзакции с тем оператором, который его вызвал.
Рис. 1. Схема доступа к базе данных |
Проще говоря, это означает, что исполнение оператора SQL может вызвать исполнение методов Java, и в то время как этот оператор и эти методы исполняются, методы могут обращаться назад к базе данных (рис.1).
Процедуры Java, возвращающие результирующие наборы
Ранее мы обсуждали, каким образом процедура SQL article_count могла бы возвращать один или более результирующих наборов вместе с ее параметрами OUT или INOUT. Процедуры Java также могут возвращать результирующие наборы, создавая объекты JDBC ResultSet и возвращая их SQL. Это показывает следующий пример:
public static void ArticleCount (int volume,ResultSet[] rs) throws SQLException { String query = «SELECT no, COUNT(*) « + «FROM sigmod_articles sa « + «WHERE sa.volume = ? « + «GROUP BY no»; Connection conn = DriverManager.getConnection («jdbc:default:connection»); PreparedStatement pstmt = conn.prepareStatement(query); pstmt.setInt(1, volume); rs[0] = pstmt.executeguery(); } CREATE PROCEDURE j_article_count (IN INTEGER volume) READS SQL DATA DYNAMIC RESULT SETS 1 EXTERNAL NAME ?sigmod_jar:ArticleCount (int, ResultSet[])? LANGUAGE JAVA PARAMETER STYLE JAVA ;
Этот пример можно было бы переписать, используя операторы SQLJ Части 0 вместо методов JDBC. В таком случае SQLJ Часть 1 позволила бы возвратить sqlj.runtime.ResultSetIterator вместо ResultSet.
ПУТИ SQL-JAVA
Вызов Java-программы приводит к вызову соответствующего ей метода Java. Этот метод был однозначно идентифицирован в операторе CREATE FUNCTION или CREATE PROCEDURE, который ее создал, именем Jar-файла, именем метода и сигнатурой. Весьма вероятно, что такие методы сами также будут содержать вызовы методов Java. В большинстве JVM существует некоторая переменная среды - путь доступа к классам (classpath), определяющая порядок, которому необходимо следовать при просмотре каталогов, Jar-файлов и Zip-файлов для поиска нужного метода.
В SQLJ Части 1 для каждого Jar-файла существует аналогичная переменная SQL-Java Path, которая служит для определения порядка просмотра содержащихся в нем файлов в случае, когда осуществляется вызов некоторого метода. Кроме того, имеется процедура sqlj.after_java_path, позволяющая модифицировать SQL-Java path.
CALL sqlj.alter_java_path (?sigmod_jar?, ?(Utility/Soundex, util_jar) (Sigplan, sigplan_jar)? );
Второй аргумент этой процедуры содержит пары, каждая из которых состоит из шаблона имени класса и имени Jar-файла. При вызове какого-либо метода первая из пар, которая содержит шаблон, соответствующий имени класса нужного метода, будет определять, какой Jar-файл следует использовать.
После того, как этот процесс был выполнен, вызов метода Utility.Soundex.isLike некоторым методом из Jar-файла sigmod_jar примет файл util_jar за местонахождение вызываемого метода, даже если sigplan_jar также содержит метод с тем же самым именем.
ДЕСКРИПТОРЫ РАЗВЕРТЫВАНИЯ
Для того, чтобы можно было использовать Java-программы с помощью операторов SQL, необходимо выполнить следующие шаги:
- установить Jar-файл;
- создать Java-программы, которые соответствуют методам с атрибутами public static;
- предоставить доступ к этим методам Java.
Дескрипторы (описатели) развертывания позволяют непосредственно в самом Jar-файле определить те шаги, которые следуют за его установкой, и обеспечить их автоматическое выполнение. Дескриптор развертывания содержит ряд действий, которые должны выполняться при установке Jar-файла, а также ряд действий, осуществляемых при удалении такого файла. Так как SQL-имя Jar-файла неизвестно в то время, когда пишется дескриптор развертывания, то для идентификации методов Java используется метка-заполнитель (placeholder) thisjar. Мы могли бы создать файл sigmod_deploy.txt, содержащий следующий текст, и поместить его в наш Jar-файл.
SQLActions[] = { «BEGIN INSTALL CREATE FUNCTION j_pub_year (INTEGER volume) RETURNS CHAR (4) EXTERNAL NAME ?thisjar:pubYear? LANGUAGE JAVA PARAMETER STYLE JAVA ; GRANT EXECUTE ON j_pub_year TO PUBLIC; END INSTALL», «BEGIN REMOVE REVOKE EXECUTE ON j_pub_year FROM PUBLIC RESTRICT ; DROP FUNCTION j_pub_year RESTRICT ; END REMOVE» }
Этот файл идентифицировался бы тогда следующей записью-манифестом в нашем Jar-файле:
Name: sigmod_deploy.txt SQLJDeploymentDescriptor: TRUE
Следующая процедура sqlj.install_jar будет обрабатывать указанные предложения в дескрипторе развертывания, поскольку в ней в качестве последнего аргумента используется 1.
CALL sqlj.install_jar (?file:///d:/Sigmod.jar?, ?sigmod_jar?, 1);
ЗАКЛЮЧЕНИЕ
В проекте SQL/PSM был принят подход, ориентированный на базы данных. Поскольку при этом признавалось, что в базе данных необходимо поддерживать поведение, то для этих целей были созданы процедурные языки (Oracle имеет PL/SQL, Sybase - язык TransactSQL, а Informix использует Stored Procedure Language, или SPL). Эти языки были разработаны для того, чтобы «бесшовно» работать с SQL. В них поддерживаются типы данных SQL, неопределенные значения и обработка исключительных ситуаций SQL. В связи с тем, что в этих языках не предусмотрены управление памятью и указатели, они не могут подвергать риску безопасность и помехоустойчивость СУБД.
Было также признано, что пользователи хотели бы иметь возможность использовать имеющийся в их распоряжении программный код. Для этих целей была предусмотрена концепция внешней программы. Этот механизм удовлетворяет предпочтения пользователей к работе с конкретным языком, и может быть полезен в тех случаях, когда некоторые программы оказывается слишком трудно писать на SQL/PSM.
SQLJ Часть 1 позволяет проектировщику базы данных повторно использовать классы Java, которые могли быть первоначально написаны для какого-либо клиентского или серверного приложения. При разработке Java-программ, специально написанных для базы данных, проектировщик базы данных может опереться на свои знания Java и воспользоваться преимуществом сред разработки и инструментальных средств, которые существуют для Java. В отличие от внешних программ, написанных на других языках третьего поколения (3GL), безопасность и помехоустойчивость не подвергаются риску при использовании Java-программ.
ПРИОБРЕТЕНИЕ СПЕЦИФИКАЦИЙ SQLJ
Спецификацию SQLJ Часть 1 будет можно заказать на сайте NCITS: http://www.cssinfo.com/ncits.html
Американский национальный институт стандартов
(American National Standards Institute, ANSI) http://web.ansi.org |
Национальный комитет по стандартам информационных технологий (National Committee for Information Technology Standards, NCITS) http://www.ncits.org |
Комитет по базам данных NCIST
(NCITS H2 - Database Committee) http://www.ncits.org/tc_home/h2.htm |
Группа SQLJ http://www.sqlj.org/ |
Литература
[1] SQLJ Part 0, now known as SQL/OLB (Object-Language Bindings), Andrew Eisenberg and Jim Melton, ACM SIGMOD Record, Decemder 1998. Есть русск. пер.: Э. Эйзенберг, Дж. Мелтон. Связывания для объектных языков: SQLJ Часть 0, называемая теперь SQL/OLB. Открытые системы, 4, 1999.
[2] ANSI X3.135.10:1998, Database Language SQL - Part 10: Object Language Bindings (SQL/OLB), 1998.
[3] ANSI NCITS 331.1-1999, SQLJ - Part I: SQL Routines using the Java Programming Language, 1999.
[4] ISO/IEC 9075:1996, Information technology - Database languages - SQL - Part 4: Persistent Stored Modules (SQL/PSM), 1996.
[5] New Standard for Stored Procedures in SQL, Andrew Eisenberg, SIGMOD Record, December 1996. Есть русск. пер.: Э. Айзенберг. Новый стандарт хранимых процедур в языке SQL. «СУБД», 5-6/1996.
Andrew Eisenberg (Progress Software Corp.), Jim Melton (Oracle Corp.) SQLJ - Part I: SQL Routines using the Java Programming Language. SIGMOD Record, Vol.28, No.4, December 1999.