Light scripting in BroadSQL: the JS command family
September 4, 2026
A new lightweight JavaScript layer lets a BroadSQL script hold two or more database connections open at once and drive them with ordinary loops and conditionals, something the interactive shell cannot do at all.
The problem: one connection at a time
BroadSQL's interactive shell holds exactly one live connection at a time: CONNECT
always closes whatever was open before opening the new one. That keeps day-to-day work simple, but it
makes an entire category of task structurally impossible without leaving BroadSQL: anything that needs
two databases open at once (comparing, migrating, reconciling), or a loop with conditional logic
driving several statements. Until now, the only options were typing every statement by hand, one at a
time, or writing a full compiled Java extension for what is really a five-line automation.
The BroadSQL approach: a script that can hold several connections at once
The new JS command family runs a plain JavaScript file, or a one-line snippet typed
directly into the shell, against a small set of BroadSQL-provided functions: connect(name)
to open one or more independent database connections, db to reuse whichever connection is
already active interactively, .execute(sql)/.executeUpdate(sql) to run
queries, and print/println for output. It runs on standalone Nashorn
(org.openjdk.nashorn:nashorn-core), the JavaScript engine that used to ship inside the JDK,
wired in through the standard Java scripting API. No compilation, no plugin JAR to build or drop into a
folder, no restart: save a text file, type one command, see the result.
Two commands cover how you run one: JS RUN <file> [args...] runs a saved
.js file, resolved the same way SCRIPT RUN resolves a .sql file
today (though, unlike SQL scripts, the .js extension is not optional to type). Any extra
tokens after the file name are passed into the script as args[0], args[1],
and so on. JS EVAL <code> is the no-file equivalent, for a snippet you want to try
immediately.
A first script
The smallest useful script is a handful of lines:
-- @description: Prints today's row count from the CUSTOMER table
if (db === null) {
println("No active connection. Connect first, then run this script.");
} else {
var rows = db.execute("SELECT COUNT(*) AS CNT FROM CUSTOMER");
println("CUSTOMER table currently has " + rows.get(0)["CNT"] + " rows.");
}
Connect to a database as you normally would, then run it: JS RUN hello_db.js;. On its
own, a script this small doesn't buy you much over typing the equivalent SELECT directly.
Its point 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 is small, and that's exactly where the real
value shows up.
Two connections at once
This is the example that demonstrates the actual gap described above: two live database connections open at the same time.
-- migrate_active_customers.js
-- Copies every CLOSED order with a positive amount from PROD_ORDERS into ARCHIVE_DB.
var source = connect('PROD_ORDERS');
var target = connect('ARCHIVE_DB');
var rows = source.execute(
"SELECT id, customer, amount, status FROM orders WHERE status = 'CLOSED'"
);
var migrated = 0;
for (var i = 0; i < rows.size(); i++) {
var row = rows.get(i);
if (row["amount"] > 0) {
target.executeUpdate(
"INSERT INTO orders_archive (id, customer, amount, status) VALUES (" +
row["id"] + ", '" + row["customer"] + "', " + row["amount"] + ", '" + row["status"] + "')"
);
migrated++;
}
}
println("Migrated " + migrated + " of " + rows.size() + " closed orders.");
source.close();
target.close();
Run it with JS RUN migrate_active_customers.js;, or pass the two connection names as
arguments instead of hard-coding them (JS RUN migrate_active_customers.js PROD_ORDERS
ARCHIVE_DB;), reading them inside the script as args[0]/args[1], so the
same file works against any pair of environments.
Walking through it: connect('PROD_ORDERS') and connect('ARCHIVE_DB') each
open a separate, independent connection, looked up from the same connection registry
CONNECT/SHOW ALL CONNECTIONS already use. Neither touches the other, and
neither touches whatever is connected interactively, if anything. source.execute(...) runs
the SELECT and reads every matching row into memory up front, so rows becomes
an ordinary collection you can index with rows.get(i) and read by column name with
row["amount"]. The for loop 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: for every row pulled from the source, it checks a condition in ordinary JavaScript, and if it
holds, builds and runs an INSERT against the target connection, a database entirely
separate from where the row came from. If either connection fails to open, or a statement 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: a script never crashes the shell.
Honest scope
There is no sandbox: a script runs at the same trust level as an extension JAR or a .bat
file you already run, with full access to the JVM through JavaScript's Java interop. 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 that never calls into a database, though it still cancels a script blocked on a slow
query, same as any other command. And execute(sql) reads a whole result set into memory
before returning it: fine for ordinary sized results, not a fit for a query that returns millions of
rows. None of this is accidental; each limit is a deliberate first-version boundary, not an oversight.
Who this is for
Anyone already comfortable in BroadSQL who has hit the "I need two databases open at once" wall: data stewards migrating or reconciling data between environments, and ops engineers automating a multi-step check that a single SQL statement can't express. It sits below a full custom command JAR (still the right tool for a command shipped to every user of that JAR) and above hand-typing statements one at a time.
Get your automation scripted properly
I write and review JS scripts for teams moving a recurring, multi-database job (a
migration, a reconciliation, a sync check) out of manual steps and into a version-controlled file that
runs the same way every time. Get in touch to talk about the job you're
still doing by hand.
BroadSQL