Loading...
MySQL 9.5 Reference Manual 9.5의 27.3.8 Using JavaScript Libraries의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
이 절에서는 MySQL Enterprise Edition의 Multilingual Engine (MLE)이 지원하는 JavaScript 저장 프로그램에서 JavaScript 라이브러리를 사용하는 방법과 예시를 제공합니다. (자세한 내용은 Section 7.5.7, “Multilingual Engine Component (MLE)”를 참조하십시오).
먼저 jslib 데이터베이스를 생성하고, 다음과 같이 현재 데이터베이스로 설정합니다:
1mysql> CREATE DATABASE IF NOT EXISTS jslib; 2Query OK, 0 rows affected (0.02 sec) 3 4mysql> USE jslib; 5Database changed
여기 제시된 두 개의 CREATE LIBRARY 문을 사용하여, 각각 하나의 함수를 익스포트하는 두 개의 JavaScript 라이브러리를 생성합니다. 임포트 가능하려면, 함수는 export 키워드로 선언되어야 합니다. (이는 다른 루틴으로 임포트하려는 모든 JavaScript 값에 대해 마찬가지입니다. 자세한 내용은 Mozilla Developer 문서의 export를 참조하십시오).
1mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib1 LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> export function f(n) { 4 $> return n 5 $> } 6 $> $$; 7Query OK, 0 rows affected (0.02 sec) 8 9mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib2 LANGUAGE JAVASCRIPT 10 -> AS $$ 11 $> export function g(n) { 12 $> return n * 2 13 $> } 14 $> $$; 15Query OK, 0 rows affected (0.00 sec)
하나의 라이브러리 안에서 하나의 함수를 선택적으로 export default로 선언할 수 있습니다. 이 경우, 임포트하는 루틴에서 해당 함수는 libname.default()로 호출해야 합니다.
JavaScript 라이브러리에 대한 일반적인 정보는 Information Schema LIBRARIES 테이블을 조회하여 얻을 수 있으며, Information Schema ROUTINE_LIBRARIES 테이블은 저장 루틴으로의 임포트를 보여줍니다. 이 두 테이블에서 jslib.lib1 및 jslib.lib2 라이브러리에 해당하는 행은 다음 쿼리로 확인할 수 있습니다:
1mysql> SELECT * FROM information_schema.LIBRARIES 2 -> WHERE LIBRARY_SCHEMA='jslib'\G 3*************************** 1. row *************************** 4 LIBRARY_CATALOG: def 5 LIBRARY_SCHEMA: jslib 6 LIBRARY_NAME: lib1 7LIBRARY_DEFINITION: 8 export function f(n) { 9 return n 10 } 11 12 LANGUAGE: JAVASCRIPT 13 CREATED: 2024-12-16 09:20:26 14 LAST_ALTERED: 2024-12-16 09:20:26 15 SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE, 16NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION 17 CREATOR: me@localhost 18*************************** 2. row *************************** 19 LIBRARY_CATALOG: def 20 LIBRARY_SCHEMA: jslib 21 LIBRARY_NAME: lib2 22LIBRARY_DEFINITION: 23 export function g(n) { 24 return n * 2 25 } 26 27 LANGUAGE: JAVASCRIPT 28 CREATED: 2024-12-16 09:20:26 29 LAST_ALTERED: 2024-12-16 09:20:26 30 SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE, 31NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION 32 CREATOR: me@localhost 332 rows in set (0.00 sec) 34 35mysql> SELECT * FROM information_schema.ROUTINE_LIBRARIES 36 -> WHERE LIBRARY_SCHEMA='jslib'\G 37*************************** 1. row *************************** 38ROUTINE_CATALOG: def 39 ROUTINE_SCHEMA: jslib 40 ROUTINE_NAME: foo 41 ROUTINE_TYPE: FUNCTION 42LIBRARY_CATALOG: def 43 LIBRARY_SCHEMA: jslib 44 LIBRARY_NAME: lib1 45LIBRARY_VERSION: NULL 46*************************** 2. row *************************** 47ROUTINE_CATALOG: def 48 ROUTINE_SCHEMA: jslib 49 ROUTINE_NAME: foo 50 ROUTINE_TYPE: FUNCTION 51LIBRARY_CATALOG: def 52 LIBRARY_SCHEMA: jslib 53 LIBRARY_NAME: lib2 54LIBRARY_VERSION: NULL 552 rows in set (0.00 sec)
두 번째 쿼리는 “어떤 저장 루틴들이 jslib에서 임포트하며, 무엇을 임포트하는가?”라는 질문에 대한 답을 제공합니다.
LIBRARIES 및 ROUTINE_LIBRARIES 테이블은 MLE 컴포넌트에서 제공하며, 이 컴포넌트가 설치되어 있지 않으면 존재하지 않습니다.
필요한 권한이 있는 경우, SHOW CREATE LIBRARY 문을 사용하여 라이브러리의 JavaScript 코드를 볼 수도 있습니다. 자세한 정보와 예시는 해당 문 설명을 참조하십시오.
SHOW LIBRARY STATUS를 사용하여 하나 이상의 JavaScript 라이브러리에 대한 이름, 데이터베이스, 생성자(definer), 생성 및 최근 수정 일자 등의 기본 정보를 얻을 수 있습니다. 자세한 정보와 예시는 Section 15.7.7.25, “SHOW LIBRARY STATUS Statement”를 참조하십시오.
두 라이브러리를 사용하는 JavaScript 함수를 생성하려면, CREATE FUNCTION의 일부로 임포트할 라이브러리 목록과 함께 USING 키워드를 포함하면 됩니다. 예시는 다음과 같습니다:
1mysql> CREATE FUNCTION foo(n INTEGER) RETURNS INTEGER LANGUAGE JAVASCRIPT 2 -> USING (jslib.lib1 AS mylib, jslib.lib2 AS yourlib) 3 -> AS $$ 4 $> return mylib.f(n) + yourlib.g(n) 5 $> $$; 6Query OK, 0 rows affected (0.00 sec)
별칭(AS 키워드와 절)은 일반적으로 선택 사항이지만, 지정한 경우에는 자신의 저장 프로그램에서 해당 라이브러리의 함수를 사용할 때 이 별칭을 라이브러리 이름으로 사용해야 합니다. 라이브러리 식별자(이름, 또는 존재하는 경우 데이터베이스 이름을 제외한 별칭)는 주어진 JavaScript 저장 함수 내에서 고유해야 합니다. CREATE FUNCTION에서 AS를 사용하여 라이브러리 간의 이름 충돌을 피할 수 있습니다. 예를 들어, 현재 데이터베이스에 ourlib라는 이름의 라이브러리와, 동일한 이름이지만 other 데이터베이스에 있는 라이브러리를 함께 포함하려면, 다음과 같은 문을 사용할 수 있습니다:
1CREATE FUNCTION myfunc(x INTEGER) RETURNS INTEGER LANGUAGE JAVASCRIPT 2 USING (ourlib, other.ourlib AS theirlib) 3... 4;
방금 본 경우처럼 같은 이름을 가진 라이브러리가 두 개 있기 때문에, 충돌을 방지하려면 둘 중 적어도 하나에는 별칭을 사용하는 것이 필요합니다.
포함된 라이브러리 중 하나(또는 그 이상)가 존재하지 않거나, 사용자가 해당 라이브러리에 접근할 수 있는 필요한 권한을 가지고 있지 않으면, 이를 참조하는 CREATE FUNCTION 문은 에러와 함께 거부됩니다.
JavaScript 저장 루틴 내에서 임포트된 라이브러리에 대한 참조는 선언된 라이브러리 이름과 일치해야 합니다. USING 절에서 사용하는 이름은 동일한 대소문자를 가질 필요는 없다는 점에 유의하십시오. 예를 들어, USING (MY_LIB)는 my_lib라는 이름의 라이브러리를 임포트하는 데 사용할 수 있지만, 저장 루틴 본문 내에서 라이브러리를 참조할 때는 my_lib를 사용해야 합니다.
다음과 같이, Information Schema ROUTINES 테이블을 조회하여 함수가 생성되었는지 확인할 수 있습니다:
1mysql> SELECT 2 -> SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_SCHEMA, 3 -> DATA_TYPE, ROUTINE_DEFINITION 4 -> FROM information_schema.ROUTINES 5 -> WHERE ROUTINE_NAME='foo'\G 6*************************** 1. row *************************** 7 SPECIFIC_NAME: foo 8 ROUTINE_NAME: foo 9 ROUTINE_SCHEMA: jslib 10 DATA_TYPE: int 11ROUTINE_DEFINITION: 12 return mylib.f(n) + otherlib.g(n) 131 row in set (0.00 sec)
방금 생성한 함수는 다른 저장 함수와 마찬가지 방식으로 호출할 수 있습니다.
1mysql> SELECT foo(2), foo(3), foo(-10), foo(1.5), foo(1.2); 2+--------+--------+----------+----------+----------+ 3| foo(2) | foo(3) | foo(-10) | foo(1.5) | foo(1.2) | 4+--------+--------+----------+----------+----------+ 5| 6 | 9 | -30 | 6 | 3 | 6+--------+--------+----------+----------+----------+ 71 row in set (0.00 sec)
입력 파라미터의 타입이 INTEGER이기 때문에, 값이 어떤 계산에 사용되기 전에 Math.round()를 사용하는 것처럼 반올림이 수행됩니다. 따라서 1.5는 2 + (2 * 2) = 6으로, 1.2는 1 + (2 * 1) = 3으로 평가됩니다.
JavaScript 구문은 라이브러리 생성 시점에 검사되며, 다음과 같이 동작합니다:
1mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib3 LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> export function f(n) { 4 $> return n $ 2 5 $> } 6 $> $$; 7ERROR 6113 (HY000): JavaScript> SyntaxError: lib3:3:17 Expected ; but found $ 8 return n $ 2 9 ^
CREATE LIBRARY 문은 오탈자를 수정한 후에는 다음과 같이 정상적으로 실행됩니다:
1mysql> CREATE LIBRARY IF NOT EXISTS jslib.lib3 LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> export function h(n) { 4 $> return n - 2 5 $> } 6 $> $$; 7Query OK, 0 rows affected (0.01 sec)
USING 절로 지정할 필요가 없는 동적 임포트를 수행하는 것도 가능합니다. 동적 임포트는 Promise를 반환한다는 점에 유의해야 합니다. 임포트된 라이브러리를 얻으려면 await을 사용하십시오. 일반적으로 코드에서 생성된 모든 Promise에 대해 await를 사용해 완료를 기다리는 것이 권장됩니다.
다음과 같이 저장 함수 및 저장 프로시저의 최상위 레벨에서 await를 사용할 수 있습니다:
1mysql> CREATE DATABASE db1; 2Query OK, 0 rows affected (0.01 sec) 3 4mysql> CREATE LIBRARY db1.lib1 LANGUAGE JAVASCRIPT 5 -> AS $$ 6 $> export function myAdd(x, y) {returns x + y} 7 $> $$; 8Query OK, 0 rows affected (0.01 sec) 9 10mysql> CREATE FUNCTION use_dynamic_import() RETURNS INT LANGUAGE JAVASCRIPT 11 -> AS $$ 12 $> let m = await import("/db1/lib1") 13 $> return m.myAdd(1, 2) 14 $> $$; 15Query OK, 0 rows affected (0.01 sec) 16 17mysql> SELECT use_dynamic_import(); 18+-----------------------+ 19| uses_dynamic_import() | 20+-----------------------+ 21| 3 | 22+-----------------------+ 231 row in set (0.00 sec)
await를 사용하면 import()가 반환하는 Promise가 리졸브됩니다. 리졸브 상태는 pending, fulfilled, rejected 중 하나일 수 있습니다. “resolved” 또는 “settled” Promise란 더 이상 pending 상태가 아니며, fulfilled 또는 rejected 상태인 것을 의미합니다.
import()는 문자열 형식의 "/db_name/lib_name" 형태인 임포트 대상 라이브러리의 경로를 인자로 받으며, ECMAScript 모듈의 Promise를 반환합니다.
다음 예시는 런타임에 여러 라이브러리 중 어떤 것을 로드할지 결정하는 방법을 보여줍니다. 먼저, 여러 함수와 객체를 익스포트하고 default 익스포트도 가지는 두 개의 라이브러리를 다음과 같이 생성합니다:
1mysql> CREATE LIBRARY db1.lib_rectangle LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> export class Rectangle { 4 $> constructor(height, width) { 5 $> this.height = height 6 $> this.width = width 7 $> } 8 $> print() { 9 $> return "Rectangle of size " + this.height + " by " + this.width 10 $> } 11 $> } 12 $> export function area(x) {return x.height * x.width} 13 $> const r = new Rectangle(2, 3) 14 $> export default r 15 $> $$; 16Query OK, 0 rows affected (0.01 sec) 17 18mysql> CREATE LIBRARY db1.lib_square LANGUAGE JAVASCRIPT 19 -> AS $$ 20 $> export class Square { 21 $> constructor(a) { 22 $> this.a = a 23 $> } 24 $> print() {return "Square of size " + this.a} 25 $> } 26 $> export function area(x) {return x.a * x.a} 27 $> const s = new Square(2) 28 $> export default s 29 $> $$; 30Query OK, 0 rows affected (0.01 sec)
printObject() 함수는 다음과 같이 전달된 값에 따라 런타임에 임포트할 라이브러리를 결정합니다:
1mysql> CREATE FUNCTION printObject(object_type VARCHAR(16)) RETURNS TEXT LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> let module = await import(`/db1/lib_${object_type}`) 4 $> // both libraries have default exports with print methods 5 $> return module.default.print() 6 $> $$; 7Query OK, 0 rows affected (0.01 sec) 8 9mysql> SELECT printObject("square"); 10+-----------------------+ 11| printObject("square") | 12+-----------------------+ 13| Square of size 2 | 14+-----------------------+ 151 row in set (0.00 sec) 16 17mysql> SELECT printObject("rectangle"); 18+--------------------------+ 19| printObject("rectangle") | 20+--------------------------+ 21| Rectangle of size 2 by 3 | 22+--------------------------+ 231 row in set (0.00 sec)
또한, Promise를 await한 뒤 반환되는 네임스페이스 객체는 다른 객체처럼 디스트럭처링할 수 있으며, 다음 예시와 같이 default 및 다른 익스포트를 임포트하는 저장 프로그램 내에서 사용하기 쉽도록 이름을 변경할 수도 있습니다:
1mysql> CREATE FUNCTION computeRectangle() RETURNS INT LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> let {default: myRectangle, area: area} = await import(`/db1/lib_rectangle`) 4 $> return area(myRectangle) 5 $> $$; 6Query OK, 0 rows affected (0.01 sec) 7 8mysql> SELECT computeRectangle(); 9+--------------------+ 10| computeRectangle() | 11+--------------------+ 12| 6 | 13+--------------------+ 141 row in set (0.00 sec)
다음 예시에서처럼 다른 라이브러리에 라이브러리 또는 그 일부를 임포트하는 것도 가능합니다. 여기서는 함수 foo()를 라이브러리 mylib에서 라이브러리 theirlib로 임포트하여, theirlib에 정의된 함수 bar()에서 사용한 다음, 이 theirlib를 임포트하는 저장 함수 myfunc()에서 bar()를 호출합니다:
1mysql> CREATE LIBRARY mylib LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> export function foo(){return 42} 4 $> $$; 5Query OK, 0 rows affected (0.04 sec) 6 7mysql> CREATE LIBRARY theirlib LANGUAGE JAVASCRIPT 8 -> AS $$ 9 $> import {foo} from "/db1/mylib" 10 $> export function bar(){return 2 * foo()} 11 $> $$; 12Query OK, 0 rows affected (0.01 sec) 13 14mysql> CREATE FUNCTION myfunc(x INTEGER) RETURNS INT 15 -> LANGUAGE JAVASCRIPT 16 -> NO SQL 17 -> USING (theirlib) 18 -> AS $$ 19 $> let result = theirlib.bar() 20 $> 21 $> result += x 22 $> 23 $> return result 24 $> $$; 25Query OK, 0 rows affected (0.00 sec) 26 27mysql> SELECT myfunc(1), myfunc(10); 28+-----------+------------+ 29| myfunc(1) | myfunc(10) | 30+-----------+------------+ 31| 85 | 94 | 32+-----------+------------+ 331 row in set (0.00 sec)
라이브러리 함수는 해당 라이브러리 또는 그 라이브러리가 임포트된 저장 루틴 내부에서만 호출할 수 있습니다. 예를 들어, 다음 저장 함수 myfunc2()는 theirlib를 임포트하고, theirlib는 mylib를 임포트합니다. 이 경우 CREATE FUNCTION 문은 성공하지만, mylib에서 비롯된 함수를 직접 호출하려는 시도는 런타임에 다음과 같이 거부됩니다:
1mysql> CREATE FUNCTION myfunc2(x INTEGER) RETURNS INT 2 -> LANGUAGE JAVASCRIPT 3 -> NO SQL 4 -> USING (theirlib) 5 -> AS $$ 6 $> return mylib.foo() 7 $> $$; 8Query OK, 0 rows affected (0.00 sec) 9 10mysql> SELECT myfunc2(1), myfunc2(10); 11ERROR 6113 (HY000): JavaScript> ReferenceError: mylib is not defined
MLE JavaScript 라이브러리 코드는 라이브러리를 포함하는 저장 루틴의 일부로 호출될 때만 실행됩니다. 다음 문들로는 라이브러리 코드가 실행되지 않습니다:
예를 들어, 실제로 코드가 실행되지 않기 때문에 다음 CREATE LIBRARY 및 CREATE FUNCTION 문은 유효합니다:
1mysql> CREATE LIBRARY my_lib LANGUAGE JAVASCRIPT 2 -> AS $$ 3 $> throw "MyError" 4 $> $$; 5Query OK, 0 rows affected (0.02 sec) 6 7mysql> CREATE FUNCTION my_func(x INTEGER) 8 -> RETURNS INTEGER LANGUAGE JAVASCRIPT NO SQL 9 -> USING(my_lib) 10 -> AS $$ 11 $> return x * 10 12 $> $$; 13Query OK, 0 rows affected (0.02 sec)
라이브러리를 임포트하는 함수를 호출하면 실제로 라이브러리 코드가 실행되며, 다음과 같이 에러가 발생합니다:
1mysql> SELECT my_func(8); 2ERROR 6113 (HY000): JavaScript> MyError
27.3.7 Using the JavaScript SQL API
27.3.9 Using WebAssembly Libraries