This section contains examples illustrating a number of different aspects of using JavaScript programs under various circumstances.
The following example demonstrates the use of a JavaScript stored
function with table column values. First we define a stored
function gcd()
which finds the greatest common
denominator of two integers, shown here:
mysql> CREATE FUNCTION gcd(a INT, b INT)
-> RETURNS INT NO SQL LANGUAGE JAVASCRIPT AS
-> $mle$
$> let x = Math.abs(a)
$> let y = Math.abs(b)
$> while(y) {
$> var t = y
$> y = x % y
$> x = t
$> }
$> return x
$> $mle$
-> ;
Query OK, 0 rows affected (0.01 sec)
We can test the stored function, like this:
mysql> SELECT gcd(75, 220), gcd(75, 225);
+--------------+--------------+
| gcd(75, 220) | gcd(75, 225) |
+--------------+--------------+
| 5 | 75 |
+--------------+--------------+
1 row in set (0.00 sec)
Next we create a table t1
with two integer
columns and populate it with a few rows, like this:
mysql> CREATE TABLE t1 (c1 INT, c2 INT);
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO t1 VALUES ROW(12,70), ROW(17,3), ROW(81,9);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> TABLE t1;
+------+------+
| c1 | c2 |
+------+------+
| 12 | 70 |
| 17 | 3 |
| 81 | 9 |
+------+------+
3 rows in set (0.00 sec)
Now we can select from t1
, using the
gcd()
function with the column values serving
as argument values in the function call, as shown here:
mysql> SELECT c1, c2, gcd(c1, c2) AS G
-> FROM t1
-> WHERE gcd(c1, c2) > 1
-> ORDER BY gcd(c1, c2);
+----+----+---+
| c1 | c2 | G |
+----+----+---+
| 12 | 70 | 2 |
| 81 | 9 | 9 |
+----+----+---+
8 rows in set (0.01 sec)
An argument value that is not of the specified type is coerced to the correct type when possible, as shown here:
mysql> SELECT gcd(500.3, 600), gcd(500.5, 600);
+-----------------+-----------------+
| gcd(500.3, 600) | gcd(500.5, 600) |
+-----------------+-----------------+
| 100 | 3 |
+-----------------+-----------------+
1 row in set (0.01 sec)
Rounding of floating point values to integers is accomplished
using
Math.round()
;
in this case, 500.3 is rounded down to 500, but 500.5 is rounded
up to 501.
Next, we create a simple JavaScript stored procedure using a
CREATE PROCEDURE
statement that
includes an OUT
parameter for passing the
current date and time in a human-readable format to a user
variable. Since we are not certain how long this representation
is, we use VARCHAR(25) for the parameter's type.
mysql> CREATE PROCEDURE d1 (OUT res VARCHAR(25))
-> LANGUAGE JAVASCRIPT
-> AS
-> $$
$> let d = new Date().toString()
$> res = d
$> $$
-> ;
Query OK, 0 rows affected (0.01 sec)
We can now test the stored procedure, first verifying that the user variable @today has not yet been set to any value, like this:
mysql> SELECT @today;
+----------------------+
| @today |
+----------------------+
| NULL |
+----------------------+
1 row in set (0.01 sec)
mysql> CALL d1(@today);
ERROR 1406 (22001): Data too long for column 'res' at row 1
The procedure is syntactically valid, but the data type of the
INOUT
parameter (res
) does
not allow for a sufficient number of characters; rather than
truncating the value, the stored program rejects it. Since it is
not possible to alter procedure code in place, we must drop the
procedure and re-create it; this time we try doubling the length
specified for the INOUT
parameter:
mysql> DROP PROCEDURE d1;
Query OK, 0 rows affected (0.02 sec)
mysql> CREATE PROCEDURE d1 (OUT res VARCHAR(50))
-> LANGUAGE JAVASCRIPT
-> AS
-> $$
$> let d = new Date().toString()
$> res = d
$> $$
-> ;
Query OK, 0 rows affected (0.01 sec)
Now we can repeat the test, like this:
mysql> SELECT @today;
+----------------------+
| @today |
+----------------------+
| NULL |
+----------------------+
1 row in set (0.01 sec)
Prior to invoking the updated procedure with
CALL
, the value of
@today
remains unset, since the original
version of d1()
did not execute successfully.
The updated version runs successfully, and we see afterwards that,
this time, the value of the user variable is set as expected:
mysql> CALL d1(@today);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @today;
+-----------------------------------------+
| @today |
+-----------------------------------------+
| Mon Oct 30 2023 20:47:29 GMT+0000 (GMT) |
+-----------------------------------------+
1 row in set (0.00 sec)
The value that you obtain from running this example is likely to
differ to some extent from what is shown here, since the exact
representation of dates is dependent upon your system locale,
and possibly other settings. See the documentation for the
JavaScript
Date
object for more information.
The next example demonstrates the use of a JavaScript stored function in a trigger.
First we create a table t2
containing three
integer columns, like this:
mysql> CREATE TABLE t2 (c1 INT, c2 INT, c3 INT);
Query OK, 0 rows affected (0.04 sec)
Now we can create a trigger on this table. This must be done using
a CREATE TRIGGER
statement written
in the usual way using SQL (see Section 27.4, “Using Triggers”), but
it can make use of stored routines written in JavaScript, such as
the js_pow()
function shown earlier in this
section.
mysql> delimiter //
mysql> CREATE TRIGGER jst BEFORE INSERT ON t2
-> FOR EACH ROW
-> BEGIN
-> SET NEW.c2 = js_pow(NEW.c1, 2);
-> SET NEW.c3 = js_pow(NEW.c1, 3);
-> END;
-> //
Query OK, 0 rows affected (0.02 sec)
mysql> delimiter ;
mysql>
This trigger acts when a row is inserted into
t2
, taking the value inserted into the first
column and inserting the square of this value into the second
column, and its cube into the third. We test the trigger by
inserting a few rows into the table; since the only value that is
not thrown away is that which we supply for column
c1
, we can simply use NULL
for each of the remaining two columns, as shown here:
mysql> INSERT INTO t2
-> VALUES
-> ROW(1, NULL, NULL),
-> ROW(2.49, NULL, NULL),
-> ROW(-3, NULL, NULL),
-> ROW(4.725, NULL, NULL);
Query OK, 4 rows affected (0.01 sec)
Records: 4 Duplicates: 0 Warnings: 0
Since the function invoked by the trigger was written in
JavaScript, JavaScript rounding rules apply, so that 2.49 is
rounded down to 2, and 4.75 is rounded up to 5. We can see that
this is the case when we check the result using a
TABLE
statement:
mysql> TABLE t2;
+------+------+------+
| c1 | c2 | c3 |
+------+------+------+
| 1 | 1 | 1 |
| 2 | 4 | 8 |
| -3 | 9 | -27 |
| 5 | 25 | 125 |
+------+------+------+
4 rows in set (0.00 sec)
The following examples demonstrate some of the basics of working
with VECTOR
values in MySQL
JavaScript stored programs. We start by creating a table
v1
which contains a VECTOR
column c1
, like this:
mysql> CREATE TABLE v1 (
-> c1 VECTOR(3)
-> );
Query OK, 0 rows affected (0.02 sec)
To insert some values into this table, we create a JavaScript
stored procedure vxin
that takes as its
argument a string representation of a vector, converts it to a
VECTOR
value, and inserts it. We then call this
procedure a number of times, as shown here:
mysql> CREATE PROCEDURE vxin (IN val VARCHAR(100))
-> LANGUAGE JAVASCRIPT
-> AS $$
$> let q = "INSERT INTO v1 VALUES("
$> q += "STRING_TO_VECTOR(\"" + val + "\")"
$> q += ")"
$>
$> let s = session.sql(q)
$>
$> s.execute()
$> $$;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL vxin("[50, 100, 50]");
Query OK, 1 row affected (0.01 sec)
mysql> CALL vxin("[100, 0, -50]");
Query OK, 1 row affected (0.00 sec)
mysql> CALL vxin("[250, 350, 450]");
Query OK, 1 row affected (0.01 sec)
After v1
has been populated, the output from
TABLE v1
looks
like this:
mysql> TABLE v1;
+----------------------------+
| c1 |
+----------------------------+
| 0x000048420000C84200004842 |
| 0x0000C84200000000000048C2 |
| 0x00007A430000AF430000E143 |
+----------------------------+
3 rows in set (0.00 sec)
Next, we create a JavaScript stored procedure
vxout1
, which selects all of the rows in
v1
, retrieving the column values and
writing them to stdout
. The
CREATE PROCEDURE
statement used to
create this procedure is shown here:
mysql> CREATE PROCEDURE vxout1 ()
-> LANGUAGE JAVASCRIPT
-> AS $$
$> let s = session.sql("TABLE v1");
$>
$> let res = s.execute()
$>
$> console.log(res.getColumnNames())
$>
$> let row = res.fetchOne()
$>
$> while(row) {
$> console.log(row.toArray())
$> row = res.fetchOne()
$> }
$> $$;
Query OK, 0 rows affected (0.00 sec)
We can test this procedure like this:
mysql> SELECT mle_session_reset();
+------------------------------------------+
| mle_session_reset() |
+------------------------------------------+
| The session state is successfully reset. |
+------------------------------------------+
1 row in set (0.00 sec)
mysql> CALL vxout1();
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT mle_session_state("stdout")\G
*************************** 1. row ***************************
mle_session_state("stdout"): c1
50,100,50
100,0,-50
250,350,450
1 row in set (0.00 sec)
You might be expecting to see the same binary representations
shown in the output of the TABLE
statement. Because JavaScript treats a
VECTOR
value as an array (an
instance of
Float32Array
),
it is displayed using array format. You can use the SQL
HEX()
function to force such values
to be displayed using binary notation, if desired, like this:
mysql> CREATE PROCEDURE vxout2 ()
-> LANGUAGE JAVASCRIPT
-> AS $$
$> let s = session.sql("SELECT HEX(c1) FROM v1");
$>
$> let res = s.execute()
$>
$> console.log(res.getColumnNames())
$>
$> let row = res.fetchOne()
$>
$> while(row) {
$> console.log(row.toArray())
$> row = res.fetchOne()
$> }
$> $$;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT mle_session_reset();
+------------------------------------------+
| mle_session_reset() |
+------------------------------------------+
| The session state is successfully reset. |
+------------------------------------------+
1 row in set (0.00 sec)
mysql> CALL vxout2();
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT mle_session_state("stdout")\G
*************************** 1. row ***************************
mle_session_state("stdout"): HEX(c1)
000048420000C84200004842
0000C84200000000000048C2
00007A430000AF430000E143
1 row in set (0.00 sec)
The display format notwithstanding, VECTOR
values are handled as vectors, and not as strings or arrays, as we
can see from the following example which employs the
(HeatWave-only) DISTANCE()
function. First we create and populate a table
v2
containing two VECTOR
columns, using the SQL statements shown here:
CREATE TABLE v2 (
c1 VECTOR(3),
c2 VECTOR(3)
);
INSERT INTO v2 VALUES
ROW(STRING_TO_VECTOR("[50, 100, 50]"), STRING_TO_VECTOR("[0, 200, 0]")),
ROW(STRING_TO_VECTOR("[100, 0, -50]"), STRING_TO_VECTOR("[5, 10, 5]")),
ROW(STRING_TO_VECTOR("[250, 350, 450]"), STRING_TO_VECTOR("[-150, 1000, 50]"))
;
The following query shows the dot product of the two vectors in each row:
mysql> SELECT VECTOR_DISTANCE(c1, c2, "DOT") AS d FROM v2;
+--------+
| d |
+--------+
| 20000 |
| 250 |
| 335000 |
+--------+
3 rows in set (0.00 sec)
The dot product of two vectors is defined as the sum of the
products of their components, in order. For example, for the
vectors in the second row in table v5
([100, 0, -50]
and [5, 10,
5]
), the dot product is (100)(5) + (0)(10) +
(-50)(5) = 500 + 0 - 250 = 250
.
You can write your own JavaScript function for obtaining the dot product of two vectors, similar to this one:
mysql> CREATE FUNCTION dot_product (v1 VECTOR, v2 VECTOR)
-> RETURNS FLOAT DETERMINISTIC
-> LANGUAGE JAVASCRIPT
-> AS $$
$> if(v1.length !== v2.length)
$> throw new Error('Vectors must be of the same length')
$>
$> let dot = 0, i=0
$>
$> for(i=0; i<v1.length; i++)
$> dot += v1[i]*v2[i]
$>
$> return dot
$> $$;
mysql> SELECT dot_product(c1, c2) AS dot FROM v5\G
*************************** 1. row ***************************
dot: 20000
*************************** 2. row ***************************
dot: 250
*************************** 3. row ***************************
dot: 335000
Next, we create a JavaScript stored procedure
vxout5
that executes the same query, like this:
mysql> CREATE PROCEDURE vxout5 ()
-> LANGUAGE JAVASCRIPT
-> AS $$
$> let s = session.sql("SELECT VECTOR_DISTANCE(c1, c2, \"DOT\") AS d FROM v5");
$>
$> let res = s.execute()
$>
$> console.log(res.getColumnNames())
$>
$> let row = res.fetchOne()
$>
$> while(row) {
$> console.log(row.toArray())
$> row = res.fetchOne()
$> }
$> $$;
Query OK, 0 rows affected (0.01 sec)
When we run vxout5
(first clearing the session
state using mle_session_reset()
as
before), we can see that the column values from
v5
are handled as vectors, with the same result
as from running the query directly in the mysql
client:
mysql> SELECT mle_session_reset();
+------------------------------------------+
| mle_session_reset() |
+------------------------------------------+
| The session state is successfully reset. |
+------------------------------------------+
1 row in set (0.00 sec)
mysql> CALL vxout5();
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT mle_session_state("stdout")\G
*************************** 1. row ***************************
mle_session_state("stdout"): d
20000
250
335000
1 row in set (0.00 sec)