On this page
JavaScript scripting
JavaScript scripting is experimental: read What to know before relying on this before you depend on it.
This page is for developers. JS runs a plain JavaScript file (or a one-line snippet typed directly on the command line) with a small set of BroadSQL-provided functions for opening connections and running queries: a much lower-friction alternative to writing and compiling a full custom command JAR for something that is really a short automation.
It fills a specific gap. The interactive shell holds exactly one live connection at a time (CONNECT always closes whatever was open before), so anything that needs two databases open at once (comparing, migrating, reconciling) or a loop with conditional logic driving several statements is not possible today without leaving BroadSQL. A JS script can do both, in a plain text file you can edit, version control, and hand to a colleague, without ever opening a Java project or running a build.
SQL Scripts or JavaScript?
Most automation does not need JavaScript. SQL Scripts are the normal way to automate work in BroadSQL: a text file of SQL and BroadSQL commands with named arguments, variables, scalar query assignment (LET x = SELECT ...), messages (ECHO), output control (OUTPUT) and an error policy (ON ERROR). Start there.
JavaScript scripting is the advanced capability for what SQL Scripts intentionally do not do: loops, conditions, general-purpose logic, and several database connections open at the same time.
| Need | Use |
|---|---|
| Run a sequence of SQL statements and BroadSQL commands | SQL Script |
| Pass named values to it, keep a value read from the database | SQL Script: arguments and variables |
| Export the result of a prepared query | SQL Script with DUMP LIB |
| Repeat something for each row, or branch on a value | JavaScript |
| Read from one database and write to another in one run | JavaScript |
The two are separate: a .js file is not a Script, is not run by @ or LIB RUN, does not appear in the Scripts Library, and does not share SQL scripting variables.
Running a script
JS RUN copy_big_cities.js;
JS RUN copy_big_cities.js WORLD WORKCOPY;
JS EVAL println(2 + 2);
JS RUN <file> [args...] resolves <file> against the JsScripts catalog (see Application settings): a relative path, a declared @alias, or an unambiguous bare file name, then runs it. Unlike @ and LIB RUN, which run Scripts and never search, the extension is not optional: pass the file name with its .js extension (or a declared @alias, which matches regardless of extension). Any extra tokens after <file> are passed into the script as args (args[0], args[1], ...).
JS EVAL <code> runs a short snippet typed directly on the command line, the quick, no-file equivalent of JS RUN.
A first script
The smallest useful script is a handful of lines. Save this as hello_db.js:
-- @description: Prints the row count of the COUNTRY table
if (db === null) {
println("No active connection. Connect first, then run this script.");
} else {
var rows = db.execute("SELECT COUNT(*) AS CNT FROM COUNTRY");
println("COUNTRY table currently has " + rows.get(0)["CNT"] + " rows.");
}
Connect to the WORLD sample, then run it:
BroadSQL> CONNECT WORLD;
WORLD> JS RUN hello_db.js;
COUNTRY table currently has 254 rows.There is no compile step, no plugin folder to drop a JAR into, and no restart. Save the text file, type one command, see the result. On its own, a script this small doesn't buy you much over typing SELECT COUNT(*) FROM COUNTRY directly. The point of this example is the mechanics: the file, the JS RUN command, the db binding, println. Once those are familiar, the step from here to a script that opens two connections, loops over rows, and branches on a condition (something the interactive prompt cannot express at all) is small. That's what the example under "Two connections at once" below shows.
Try a one-off snippet with no file at all:
JS EVAL println(2 + 2);
4
The scripting API
Before running your code, BroadSQL binds the following into a fresh JavaScript context. Nothing is ever reused or shared across runs: every execution of JS RUN or JS EVAL gets a clean slate.
| Name | What it does |
|---|---|
connect(name) | Opens a new, independent connection to name (a connection already registered and visible to SHOW ALL CONNECTIONS). Does not touch, close, or get closed by whatever is connected interactively, so a script can hold several connections open at once. The script is responsible for calling .close() on it. |
db | The connection already active in the interactive session, if any (null if nothing is connected, so check before using it). Calling .close() on it does nothing: the script did not open it, so it cannot close it out from under the session. |
<connection>.execute(sql) | Runs a SELECT and returns every row read eagerly into memory: .size(), .get(i) (a row, plain key/value access by column name, e.g. row['AMOUNT']). No cursor, so not suitable for a huge result set. |
<connection>.executeUpdate(sql) | Runs an INSERT/UPDATE/DELETE/DDL statement, returns the affected row count. |
<connection>.close() | Closes a connection opened with connect(...). A script that forgets to call it leaves the connection open until BroadSQL exits. |
print(...) / println(...) | Writes to the console, exactly like a command's own output (never raw JavaScript output, so it respects logging and redirection). |
args | The trailing tokens from JS RUN <file> arg1 arg2, as args[0], args[1], ... (empty for JS EVAL). |
A syntax error, a thrown value, or a SQL error from .execute/.executeUpdate never crashes the shell: BroadSQL catches it, reports it with a line number where one is available, and the console is ready for your next command right after, the same failure contract every other command already has.
Two connections at once
This example demonstrates the actual gap described at the top of this page: two live database connections open at the same time, something the interactive shell cannot do.
-- copy_big_cities.js
-- Copies the WORLD cities of more than eight million inhabitants into the table BIG_CITY of WORKCOPY.
var source = connect('WORLD');
var target = connect('WORKCOPY');
var rows = source.execute(
"SELECT ID, NAME, POPULATION FROM CITY WHERE POPULATION > 5000000"
);
var copied = 0;
for (var i = 0; i < rows.size(); i++) {
var row = rows.get(i);
if (row["POPULATION"] > 8000000) {
target.executeUpdate(
"INSERT INTO BIG_CITY (ID, NAME, POPULATION) VALUES (" +
row["ID"] + ", '" + String(row["NAME"]).trim().replace(/'/g, "''") + "', " + row["POPULATION"] + ")"
);
copied++;
}
}
println("Copied " + copied + " of " + rows.size() + " cities above five million inhabitants.");
source.close();
target.close();
The target table must exist first: DUMP (SELECT ID, TRIM(NAME) AS NAME, POPULATION FROM CITY WHERE 1 = 0) TO WORKCOPY.BIG_CITY AS H2; creates WORKCOPY and an empty BIG_CITY. On WORLD, the script reads 24 cities and copies the 10 above eight million. Run it with JS RUN copy_big_cities.js;, or pass the two connection names as arguments instead of hard-coding them, so the same file works against any pair of environments:
JS RUN copy_big_cities.js WORLD WORKCOPY;
(reading them inside the script as args[0]/args[1] in place of the literal names).
What it does, step by step:
connect('WORLD')andconnect('WORKCOPY')each open a separate, independent connection, looked up from the same connection registryCONNECT/SHOW ALL CONNECTIONSalready use. Neither touches the other, and neither touches whatever is connected interactively, if anything.source.execute(...)runs theSELECTand reads every matching row into memory up front.rowsis then an ordinary collection:rows.size()for the count,rows.get(i)for a row, androw["POPULATION"]to read a column off that row by name.- The
forloop walks every row from the source and, for each one whosePOPULATIONis above eight million, builds and runs anINSERTagainst the target connection,WORKCOPY, a different database from where the row came from. This is the part no single SQL statement, and no sequence of statements typed one at a time at an interactive prompt, could do across two databases at once. copiedcounts how many rows actually passed the condition and were copied, separately from the total row count read from the source.printlnreports a one-line summary through the normal BroadSQL console, and both connections are closed explicitly, releasing them instead of leaving them open until the process exits.
If either connection fails to open, or the SELECT/INSERT hits a SQL error, the script stops with an error message and BroadSQL's prompt is ready for the next command right after, exactly as if any other command had failed.
Cataloging scripts
JS LIST [<searchTerm>|ALL] and JS FIND <term> work like LIB LIST/LIB FIND (see Scripts Library, which also spells out exactly what @instance/ @environment do and don't enforce): the same -- @description/@instance/@environment/@tags/ @alias/@status metadata header, the same Database Group/environment scoping (an entry tagged for a different Database Group or environment is hidden from the default view; JS LIST ALL shows everything), and the same grid output. The metadata header is skipped automatically before a script is handed to the JavaScript engine, so it never interferes with execution, even though -- is not JavaScript comment syntax.
-- @description: Copy the largest WORLD cities into the local copy
-- @instance: WORLDAPP
-- @environment: PROD
-- @tags: copy, cities, world
-- @alias: copy_cities
-- @status: stable
var source = connect('WORLD');
...
Once cataloged, JS LIST and JS FIND surface the script by its description, tags, or alias, exactly as they would a SQL library entry. Running a script whose tags don't match your current connection's Database Group or environment prints a one-line warning but still runs it; the tag is informational, not a hard gate, and this is never affected by the connection's environment being flagged Production.
JS does not currently have SHOW, EDIT, or DEL/archive-and-undo commands, only RUN, LIST, and FIND. Edit a .js file with any text editor, or BroadSQL's own EDIT <fileName>.
What to know before relying on this
- No sandbox. A script runs at the same trust level as an extension JAR or a
.batfile you already run: it can read/write files, start processes, or do anything else the JVM can do, through JavaScript's Java interop (Java.type(...)). This is not a safe way to run a script you did not write yourself. - CTRL+C cannot interrupt a script stuck in a pure JavaScript loop (e.g.
while (true) {}) that never calls into a database. CTRL+C still works to cancel a script blocked on a slow query, the same as any other command. - No result streaming.
execute(sql)reads the whole result set into memory before returning it, so avoid it for a query that returns a very large number of rows. - Production confirmation. On a connection of an Environment flagged Production, each
executeorexecuteUpdatecall that can modify the database asks for a confirmation on the console; a refusal raises an error in the script. See Production confirmation.
Related pages
- Command reference: JS commands, generated from each command's own code, always up to date.
- Application settings: the
JsScriptsBroadSQL.inikey. - Scripting overview: the SQL scripting guide, and the Scripts Library, which is separate from this catalog even though the metadata header format and scoping are similar. JavaScript is not part of the Scripts Library and is not shown in the BroadSQL Editor; edit
.jsfiles with any external editor. - Extending BroadSQL with custom commands: the heavier, compiled-JAR alternative for a command meant to ship to every user of that JAR.
Found something wrong or unclear on this page? Tell us, with the page address.