Spec-Zone.ru › MySQL 9.2

27.3.8 Использование JavaScript-библиотек

В этом разделе приводится информация и примеры использования JavaScript-библиотек в JavaScript-хранимых программах, поддерживаемых Многоязычной средой (MLE) в MySQL Enterprise Edition 9.2 и более поздних версиях. (См. Раздел 7.5.8, «Многоязыковая компонентная система (MLE)»).

Сначала мы создадим базу данных jslib и сделаем её текущей базой данных, как показано ниже:

mysql> CREATE DATABASE IF NOT EXISTS jslib;
Query OK, 0 rows affected (0.02 sec)

mysql> USE jslib;
Database changed

Используя два CREATE LIBRARY оператора, показанных здесь, мы создаём две JavaScript-библиотеки, каждая из которых экспортирует одну функцию. Чтобы функция была вызываема в рамках библиотеки, хранимая программа должна быть объявлена с ключевым словом export.

mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib1 LANGUAGE JAVASCRIPT
    ->     AS $$
    $>       export function f(n) {
    $>         return n
    $>       }
    $>     $$;
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib2 LANGUAGE JAVASCRIPT
    ->     AS $$
    $>       export function g(n) {
    $>         return n * 2
    $>       }
    $>     $$;
Query OK, 0 rows affected (0.00 sec)

Вы можете необязательно объявить одну функцию в данной библиотеке как export default. В этом случае функция должна вызываться импортирующей процедурой как libname.default().

Вы можете получить информацию о JavaScript-библиотеках, запросив две таблицы схемы информации, LIBRARIES и ROUTINE_LIBRARIES, предоставляемые MLE. Строки, соответствующие библиотекам jslib.lib1 и jslib.lib2 в этих двух таблицах, показаны в следующих запросах:

mysql> SELECT * FROM information_schema.LIBRARIES
    -> WHERE LIBRARY_SCHEMA='jslib'\G
*************************** 1. row ***************************
   LIBRARY_CATALOG: def
    LIBRARY_SCHEMA: jslib
      LIBRARY_NAME: lib1
LIBRARY_DEFINITION:
      export function f(n) {
        return n
      }

          LANGUAGE: JAVASCRIPT
           CREATED: 2024-12-16 09:20:26
      LAST_ALTERED: 2024-12-16 09:20:26
          SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,
NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
           CREATOR: me@localhost
*************************** 2. row ***************************
   LIBRARY_CATALOG: def
    LIBRARY_SCHEMA: jslib
      LIBRARY_NAME: lib2
LIBRARY_DEFINITION:
      export function g(n) {
        return n * 2
      }

          LANGUAGE: JAVASCRIPT
           CREATED: 2024-12-16 09:20:26
      LAST_ALTERED: 2024-12-16 09:20:26
          SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,
NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
           CREATOR: me@localhost
2 rows in set (0.00 sec)

mysql> SELECT * FROM information_schema.ROUTINE_LIBRARIES
    -> WHERE LIBRARY_SCHEMA='jslib'\G
*************************** 1. row ***************************
ROUTINE_CATALOG: def
 ROUTINE_SCHEMA: jslib
   ROUTINE_NAME: foo
   ROUTINE_TYPE: FUNCTION
LIBRARY_CATALOG: def
 LIBRARY_SCHEMA: jslib
   LIBRARY_NAME: lib1
LIBRARY_VERSION: NULL
*************************** 2. row ***************************
ROUTINE_CATALOG: def
 ROUTINE_SCHEMA: jslib
   ROUTINE_NAME: foo
   ROUTINE_TYPE: FUNCTION
LIBRARY_CATALOG: def
 LIBRARY_SCHEMA: jslib
   LIBRARY_NAME: lib2
LIBRARY_VERSION: NULL
2 rows in set (0.00 sec)

Для создания JavaScript-функции, использующей две библиотеки, включите ключевое слово USING вместе со списком библиотек, которые будут импортированы как часть CREATE FUNCTION, как показано ниже:

mysql> CREATE FUNCTION foo(n INTEGER) RETURNS INTEGER LANGUAGE JAVASCRIPT
    ->         USING (jslib.lib1 AS mylib, jslib.lib2 AS yourlib)
    ->         AS $$
    $>           return mylib.f(n) + yourlib.g(n)
    $>         $$;
Query OK, 0 rows affected (0.00 sec)

Псевдоним (ключевое слово и предложение AS) обычно необязателен, но если он указан, вы должны использовать его для имени библиотеки при включении функций из неё в собственные хранимые программы. Идентификатор библиотеки — имя или псевдоним (если он есть), за исключением имени базы данных — должен быть уникальным в данной JavaScript-хранимой функции. Вы можете использовать AS с CREATE FUNCTION, чтобы избежать столкновений имён между библиотеками. Например, чтобы включить библиотеку с именем ourlib в текущей базе данных вместе с библиотекой с тем же именем, но находящейся в базе данных other, вы можете использовать показанный здесь оператор:

CREATE FUNCTION myfunc(x INTEGER) RETURNS INTEGER LANGUAGE JAVASCRIPT
    USING (ourlib, other.ourlib AS theirlib)
...
;

В только что показанном случае существует две библиотеки с одинаковым именем, поэтому необходимо использовать псевдоним только для одной из них.

Если одна (или несколько) из включённых библиотек не существует или пользователь не имеет необходимых привилегий для доступа к ней, оператор CREATE FUNCTION, ссылающийся на неё, будет отклонен с ошибкой.

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

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

mysql> SELECT
    ->   SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_SCHEMA,
    ->   DATA_TYPE, ROUTINE_DEFINITION
    -> FROM information_schema.ROUTINES
    -> WHERE ROUTINE_NAME='foo'\G
*************************** 1. row ***************************
     SPECIFIC_NAME: foo
      ROUTINE_NAME: foo
    ROUTINE_SCHEMA: jslib
         DATA_TYPE: int
ROUTINE_DEFINITION:
      return mylib.f(n) + otherlib.g(n)

1 row in set (0.00 sec)

Мы можем вызвать только что созданную функцию так же, как любую другую хранимую функцию.

mysql> SELECT foo(2), foo(3), foo(-10), foo(1.5), foo(1.2);
+--------+--------+----------+----------+----------+
| foo(2) | foo(3) | foo(-10) | foo(1.5) | foo(1.2) |
+--------+--------+----------+----------+----------+
|      6 |      9 |      -30 |        6 |        3 |
+--------+--------+----------+----------+----------+
1 row in set (0.00 sec)

Поскольку входной параметр имеет тип INTEGER, округление, как если бы использовалась Math.round(), происходит перед использованием значения в каких-либо расчётах, 1.5 оценивается как 2 + (2 * 2) = 6, а 1.2 как 1 + (2 * 1) = 3.

Синтаксис JavaScript проверяется во время создания библиотеки, как показано здесь:

mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib3 LANGUAGE JAVASCRIPT
    ->     AS $$
    $>       export function f(n) {
    $>         return n $ 2
    $>       }
    $>     $$;
ERROR 6113 (HY000): JavaScript> SyntaxError: lib3:3:17 Expected ; but found $
        return n $ 2
                 ^

Оператор CREATE LIBRARY выполняется успешно после исправления опечатки, как показано здесь:

mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib3 LANGUAGE JAVASCRIPT
    ->     AS $$
    $>       export function h(n) {
    $>         return n - 2
    $>       }
    $>     $$;
Query OK, 0 rows affected (0.01 sec)

Также возможно выполнять динамический импорт, который не должен быть указан с помощью предложения USING; он поддерживается, но его использование не рекомендуется, так как динамический импорт возвращает Promise, который не разрешается до завершения синхронного блока кода. Это означает, что блок, зависящий от динамически импортируемого модуля, не может влиять на возвращаемое значение хранимой функции или параметр OUT или INOUT хранимой процедуры.

Для выполнения динамического импорта используйте глобальный метод import(), передав ему ссылку на библиотеку в формате /schema_name/library_name (символ слэша в начале обязателен), как показано здесь:

USE my_db;
CREATE LIBRARY my_lib LANGUAGE JAVASCRIPT
AS $$
  export function foo(){return 42;};
  export function thrower(){throw "MyError";};
$$;

CREATE FUNCTION my_func() RETURNS INT LANGUAGE JAVASCRIPT
AS $$
  let result = 0

  import("/my_db/my_lib").then((myModule) => {
    let {foo} = myModule

    result = foo();  // sets result to 42
  })

  // Return statement is executed before the Promise in the previous block
  //  is resolved, thus always returning 0, and not 42
  return result
$$;

# returns 0
SELECT my_func();

CREATE FUNCTION my_func() RETURNS INT LANGUAGE JAVASCRIPT
AS $$
  let result = 0

  import("/my_db/my_lib").then((myModule) => {
    let {thrower} = myModule
    thrower() // throws an exception
  })

  // Return statement is executed before the Promise in the previous block
  // is resolved, thus always returning 0...
  return result
$$;

# ...but this throws an error due to unhandled rejection of the Promise
SELECT my_func();

Можно импортировать библиотеки или их части в другие библиотеки, как показано в этом примере, где функция foo() импортируется из библиотеки mylib в библиотеку theirlib и используется в функции bar(), определённой в theirlib, которая затем импортируется в хранимую функцию myfunc(), которая вызывает bar():

mysql> CREATE LIBRARY mylib LANGUAGE JAVASCRIPT
    -> AS $$
    $>   export function foo(){return 42}
    $> $$;
Query OK, 0 rows affected (0.04 sec)

mysql> CREATE LIBRARY theirlib LANGUAGE JAVASCRIPT
    -> AS $$
    $>   import {foo} from "/db1/mylib"
    $>   export function bar(){return 2 * foo()}
    $> $$;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE FUNCTION myfunc(x INTEGER) RETURNS INT
    -> LANGUAGE JAVASCRIPT
    -> NO SQL
    -> USING (theirlib)
    -> AS $$
    $>   let result = theirlib.bar()
    $>
    $>   result += x
    $>
    $>   return result
    $> $$;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT myfunc(1), myfunc(10);
+-----------+------------+
| myfunc(1) | myfunc(10) |
+-----------+------------+
|        85 |         94 |
+-----------+------------+
1 row in set (0.00 sec)

Функции библиотек могут вызываться только в пределах библиотеки или хранимой процедуры, в которую импортирована содержащая их библиотека. Например, следующая хранимая функция myfunc2() импортирует theirlib, а theirlib импортирует mylib. Оператор CREATE FUNCTION в данном случае выполняется успешно, но прямая попытка вызова функции, происходящей из mylib, отклоняется во время выполнения, как показано здесь:

mysql> CREATE FUNCTION myfunc2(x INTEGER) RETURNS INT
    -> LANGUAGE JAVASCRIPT
    -> NO SQL
    -> USING (theirlib)
    -> AS $$
    $>   return mylib.foo()
    $> $$;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT myfunc2(1), myfunc2(10);
ERROR 6113 (HY000): JavaScript> ReferenceError: mylib is not defined

Код JavaScript-библиотеки MLE выполняется только при вызове в качестве части хранимой процедуры, которая включает библиотеку. Код библиотеки не выполняется ни одним из следующих операторов:

  • CREATE FUNCTION

  • CREATE PROCEDURE

  • CREATE LIBRARY

Например, эти являются допустимыми CREATE LIBRARY и CREATE FUNCTION операторами, так как код фактически не выполняется:

mysql> CREATE LIBRARY my_lib LANGUAGE JAVASCRIPT
    -> AS $$
    $>   throw "MyError"
    $> $$;
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE FUNCTION my_func(x INTEGER)
    -> RETURNS INTEGER LANGUAGE JAVASCRIPT NO SQL
    -> USING(my_lib)
    -> AS $$
    $>   return x * 10
    $> $$;
Query OK, 0 rows affected (0.02 sec)

Вызов функции, импортирующей библиотеку, фактически вызывает код библиотеки, что приводит к ошибке, как показано здесь:

mysql> SELECT my_func(8);
ERROR 6113 (HY000): JavaScript> MyError

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-9.2-en/srjs-libraries.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API