JavaScript Inside Oracle Database

Search for a command to run...

No comments yet. Be the first to comment.
Oracle APEX already includes Font APEX as the default icon library for Universal Theme. In most cases, that is enough. But in one of my APEX apps, I needed a few icons that were not available there, s

Looking to give your Oracle APEX mobile app a modern, native feel? Here’s a quick hack: add a floating action button that stays anchored at the bottom corner just like in popular mobile apps. This sma

APEX 26.1 has been updated to the Universal Theme. While much of the attention in this release is on AI features, I think the UT improvements are just as important for developers like me who care abou

The apex_barcode package offers a PL/SQL API to generate various types of barcodes. You can output barcodes as SVG (Scalable Vector Graphics) values or as PNG file blobs, making it easy to embed them

Oracle APEX makes it easy to get data on screen fast, but often, the default tabular output isn’t enough. What if you want to highlight statuses, add icons, or display badges all without writing compl

The DBMS_MLE package allows users to execute JavaScript code inside the Oracle Database and exchange data seamlessly between PL/SQL and JavaScript. JavaScript data types are automatically mapped to Oracle Database data types and vice versa.
DBMS_MLE provides a complete solution for using JavaScript inside the Oracle Database. It includes a JavaScript engine, a set of PL/SQL APIs for interacting with JavaScript, and a set of built-in JavaScript modules for interacting with the Oracle Database.
The DBMS_MLE package uses a security model that is based on Oracle Database privileges. Users must have the the EXECUTE DYNAMIC MLE privilege while calling any of its functions and procedures that pertain to MLE execution.
In addition, the DBMS_MLE subprograms can be executed only by users who have been granted the EXECUTE privilege on an MLE language role (for example, EXECUTE ON JAVASCRIPT) .
Note: The DBMS_MLE is a feature of Oracle Database 21c and higher. If you want to see what kind of MLE languages your database can run, just use the code below
DECLARE
ctxh dbms_mle.context_handle_t;
langs dbms_mle.languages_t;
i INTEGER;
BEGIN
ctxh := dbms_mle.create_context;
langs := dbms_mle.get_available_languages;
FOR i IN 1 .. langs.COUNT LOOP
dbms_output.put_line(langs(i));
END LOOP;
dbms_mle.drop_context(ctxh);
END;
/
PL/SQL is a powerful language that can probably accomplish most of the things that can be done using Javascript. However, if you have a complex code written in Javascript, it would probably take hours to replicate the logic in PL/SQL if you need to run it inside the database. That's when DBMS_MLE comes in handy and is very helpful.
The code below creates a hash using two parameters, client_id and client_secret and is used to authenticate an API. This is very complex and not easy to replicated using PL/SQL, but in minutes we can put inside the database and make it work.
DECLARE
ctx dbms_mle.context_handle_t;
code_snippet clob := q'~
function SHA1(msg) {
function rotate_left(n, s) {
var t4 = (n << s) | (n >>> (32 - s));
return t4;
}
function lsb_hex(val) {
var str = "";
var i;
var vh;
var vl;
for (i = 0; i <= 6; i += 2) {
vh = (val >>> (i * 4 + 4)) & 0x0f;
vl = (val >>> (i * 4)) & 0x0f;
str += vh.toString(16) + vl.toString(16);
}
return str;
}
function cvt_hex(val) {
var str = "";
var i;
var v;
for (i = 7; i >= 0; i--) {
v = (val >>> (i * 4)) & 0x0f;
str += v.toString(16);
}
return str;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if (c > 127 && c < 2048) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
var blockstart;
var i, j;
var W = new Array(80);
var H0 = 0x67452301;
var H1 = 0xefcdab89;
var H2 = 0x98badcfe;
var H3 = 0x10325476;
var H4 = 0xc3d2e1f0;
var A, B, C, D, E;
var temp;
msg = Utf8Encode(msg);
var msg_len = msg.length;
var word_array = new Array();
for (i = 0; i < msg_len - 3; i += 4) {
j =
(msg.charCodeAt(i) << 24) |
(msg.charCodeAt(i + 1) << 16) |
(msg.charCodeAt(i + 2) << 8) |
msg.charCodeAt(i + 3);
word_array.push(j);
}
switch (msg_len % 4) {
case 0:
i = 0x080000000;
break;
case 1:
i = (msg.charCodeAt(msg_len - 1) << 24) | 0x0800000;
break;
case 2:
i =
(msg.charCodeAt(msg_len - 2) << 24) |
(msg.charCodeAt(msg_len - 1) << 16) |
0x08000;
break;
case 3:
i =
(msg.charCodeAt(msg_len - 3) << 24) |
(msg.charCodeAt(msg_len - 2) << 16) |
(msg.charCodeAt(msg_len - 1) << 8) |
0x80;
break;
}
word_array.push(i);
while (word_array.length % 16 != 14) word_array.push(0);
word_array.push(msg_len >>> 29);
word_array.push((msg_len << 3) & 0x0ffffffff);
for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i];
for (i = 16; i <= 79; i++)
W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
A = H0;
B = H1;
C = H2;
D = H3;
E = H4;
for (i = 0; i <= 19; i++) {
temp =
(rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5a827999) &
0x0ffffffff;
E = D;
D = C;
C = rotate_left(B, 30);
B = A;
A = temp;
}
for (i = 20; i <= 39; i++) {
temp =
(rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ed9eba1) &
0x0ffffffff;
E = D;
D = C;
C = rotate_left(B, 30);
B = A;
A = temp;
}
for (i = 40; i <= 59; i++) {
temp =
(rotate_left(A, 5) +
((B & C) | (B & D) | (C & D)) +
E +
W[i] +
0x8f1bbcdc) &
0x0ffffffff;
E = D;
D = C;
C = rotate_left(B, 30);
B = A;
A = temp;
}
for (i = 60; i <= 79; i++) {
temp =
(rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xca62c1d6) &
0x0ffffffff;
E = D;
D = C;
C = rotate_left(B, 30);
B = A;
A = temp;
}
H0 = (H0 + A) & 0x0ffffffff;
H1 = (H1 + B) & 0x0ffffffff;
H2 = (H2 + C) & 0x0ffffffff;
H3 = (H3 + D) & 0x0ffffffff;
H4 = (H4 + E) & 0x0ffffffff;
}
var temp =
cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
return temp.toLowerCase();
}
function rot13(str) {
var input = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var output = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
var index = x => input.indexOf(x);
var translate = x => index(x) > -1 ? output[index(x)] : x;
return str.split('').map(translate).join('');
}
const Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
let output = "";
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
let i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode : function (input) {
let output = "";
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = Base64._keyStr.indexOf(input.charAt(i++));
enc2 = Base64._keyStr.indexOf(input.charAt(i++));
enc3 = Base64._keyStr.indexOf(input.charAt(i++));
enc4 = Base64._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
let utftext = "";
for (let n = 0; n < string.length; n++) {
let c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
}
var encriptKey;
var bindings = require("mle-js-bindings");
var clientId = bindings.importValue("clientId");
var clientSecret = bindings.importValue("clientSecret");
const sha1hash = SHA1(`${clientId}|${clientSecret}`);
const timestamp = String(new Date().getTime()).slice(0, 10);
const seed = `${clientId}|${clientSecret}|${timestamp}|${sha1hash}`;
const base64 = Base64.encode(seed);
encriptKey = rot13(base64);
bindings.exportValue("encriptKey", encriptKey);
~';
l_encriptKey varchar2(4000);
l_clientId varchar2(4000) := '6cec4abd-xxxx-xsce-xxxx-89caxxxx1a09';
l_clientSecret varchar2(4000) := '1lsy90db99f1acdba9e8fxxx2844354890cxxx8b7211071a29a60fe17ac8b1';
BEGIN
ctx := dbms_mle.create_context();
dbms_mle.export_to_mle(ctx, 'clientId', l_clientId);
dbms_mle.export_to_mle(ctx, 'clientSecret', l_clientSecret);
dbms_mle.eval(ctx, 'JAVASCRIPT', code_snippet);
dbms_mle.import_from_mle(ctx, 'encriptKey', l_encriptKey);
dbms_output.put_line(l_encriptKey);
dbms_mle.drop_context(ctx);
END;
The dbms_output.put_line will print the hash generated by the javascript.

It only took me a few minutes to write the PL/SQL once I understood how to use the DBMS_MLE. Let's take a closer look at what's going on.
dbms_mle.create_context(): This function returns a handle that uniquely identifies a context within a session. We are going to use this context in the next steps.
dbms_mle.export_to_mle: This procedure allow us to pass values from PL/SQL to the context to be used by the javascript
dbms_mle.eval: Like the javascript eval, this procedure will execute the javascript inside the code _snippet.
dbms_mle.import_from_mle: This procedure retrieves value generated by the javascript function to the PL/SQL code.
dbms_mle.drop_context: This procedure will drop the context that was previously created using the CREATE_CONTEXT function.