More control, fewer surprises: what BroadSQL 5.5.0 brings you

October 17, 2026

#release #production #security #scripting #configuration

A confirmation before anything can change a production database. A master password change that can no longer damage your configuration. Script parameters with types, defaults and lists, checked before a single statement runs. A Scripts Library you can browse, group and check, and settings you can edit without opening a text file. Here is how BroadSQL 5.5.0 makes everyday work calmer, and why we think you will notice it in the first hour.

It is 4:40 on a Friday. A ticket comes in: three customers say their orders have been "processing" since Monday. You open BroadSQL, connect to production, find the query you wrote the last time this happened, change the customer numbers, run it, and get an answer. Then someone on the call says "can you just reset those three to PENDING?", and you type an UPDATE.

Every step is ordinary, and every step has a way to go wrong: the wrong connection, an old copy of the query, a date typed the European way, a change made on production by reflex. BroadSQL 5.4.0 and 5.4.5 made the console a nicer place to spend the day. BroadSQL 5.5.0, published on October 17, 2026, adds something different: control. It asks before production changes, checks Script parameters before they reach the database, keeps your configuration file out of harm's way, and tells you more clearly where you are and what just happened.

Here is the release as you would meet it in a working day, with real commands and output. The Release Notes 5.5.0 list every change; for the previous releases, see A nicer place to work.

5.5.0 at a glance

A second look before production changes

Anyone who works with databases long enough has a story about the UPDATE that ran on the wrong environment. Usually it was not carelessness, just a long day, two windows that looked alike, and a statement perfectly correct for the other database.

BroadSQL has known for a while which of your Environments are production: an Environment can carry a Production flag, set in CONFIG, and every Connection belongs to an Environment. Until now, that flag changed how things looked. In 5.5.0, it changes what happens.

Up to 5.4.5

The prompt of a Production connection had its own color, and BroadSQL printed a warning when one was reopened at startup. After that, an UPDATE typed on production ran exactly like one typed on your laptop's test database.

Since 5.5.0

Before a statement that can modify a Production database, BroadSQL stops, shows the connection, the Environment, the statement and the reason it asks, and waits for yes. Nothing is sent to the database before you answer.

Here is what that looks like at the prompt. The connection SALES_PROD belongs to an Environment called PROD, flagged Production:

Console: a confirmed change
SALES_PROD> UPDATE CUSTOMER SET STATUS = 'ACTIVE' WHERE ID = 7;
WARNING: This operation can modify a Production database.
  Connection: SALES_PROD (Environment PROD, Production)
  Statement:  UPDATE CUSTOMER SET STATUS = 'ACTIVE' WHERE ID = 7
  Why:        it is an UPDATE
Execute? [yes/no]: yes
1 row(s) updated.

And here is the same statement when you answer anything else. Only yes or y, in any case, runs it; no, an empty answer, a typo, end of input and Ctrl+C all refuse it:

Console: a refused change
Execute? [yes/no]: no
Not executed.
SQL ERROR
------------------------------------------------------------
Connection : SALES_PROD
SQLState   : BSQPR
Error code : 0

Production change refused on connection 'SALES_PROD': the change was not confirmed. Nothing was executed.

Query:
1 | UPDATE CUSTOMER SET STATUS = 'ACTIVE' WHERE ID = 7
------------------------------------------------------------

The refusal is reported as an SQL error, so a Script, a log or a colleague reading your screen sees a statement that did not run, which is exactly what happened. Your answer goes to the activity log ("Production confirmation for SALES_PROD: approved" or "refused"), never to the command history, so pressing the Up arrow can never replay a yes.

How BroadSQL decides whether to ask A statement is first classified. A read runs at once. A statement that can modify runs at once on a Connection whose Environment is not flagged Production. On a Production Connection, BroadSQL asks; yes runs the statement, any other answer refuses it, and without an interactive terminal the statement is refused. Runs at once, no question Runs Statement typed, Script, Editor Can it modify? its form, not its effect Production Environment? Execute? [yes/no] console, or a dialog in the Editor Refused, nothing sent any other answer, or no terminal no: it reads no yes yes yes or y anything else
Every statement takes the same path, wherever it comes from. Reads never ask; on a Connection whose Environment is not flagged Production, nothing asks.

What counts as "can modify"

BroadSQL does not guess what a statement will do. It recognizes the forms that only read and treats everything else as a possible change, which errs on the safe side:

Never asksAlways asks
SELECT, WITH queries, SHOW, DESCRIBE, EXPLAININSERT, UPDATE, DELETE, MERGE, every DDL statement
Session statements such as SET SCHEMA or USEGRANT, REVOKE, other SET statements
COMMIT, ROLLBACK, SAVEPOINTCALL, EXEC, BEGIN blocks: BroadSQL cannot see inside them
BroadSQL commands that only readSELECT ... INTO, SELECT ... FOR UPDATE, EXPLAIN ANALYZE, and anything it cannot read

The rule applies wherever the statement comes from: the prompt, a Script, the Editor, a login script, a BroadSQL command that writes such as LOAD, JavaScript, or a / PROD rerun of your last query.

Every change, or once per session

A support engineer who changes one row a month wants the question every time; a DBA running a reviewed migration of forty statements would rather confirm once. The new setting ProductionConfirmEveryChange lets you choose:

TRUE (the default)

Asks before every statement that can modify the database. A Script with ten such statements asks ten times. A BroadSQL command counts as one: LOAD asks once for all its rows.

FALSE

Asks before the first such statement of each connected session. Any new connection asks again: CONNECT, ENV, a reconnection after a lost connection, DISCONNECT then CONNECT, and LOGOUT.

A refusal never counts as an approval. This is also the one setting that applies at once when changed in CONFIG, so you can switch to "once per session" for a planned change and back again without restarting.

What happens if...?
...a Script hits a refused statement?
The whole run stops, nested Scripts included, with a message such as "Run stopped: a Production change was refused at statement 3 of Script <name> (line 7)." Statements that already ran stay executed.
...I run it from the BroadSQL Editor?
The question is a Production database dialog with Yes and No. No is the default button, and Escape or closing the dialog also refuses.
...BroadSQL runs without a terminal, with input redirected from a file?
It cannot ask, so every statement that can modify a Production database is refused, with "confirmation is required and no interactive confirmation is available". No setting turns this off.
...I have uncommitted changes?
The confirmation never commits or rolls back anything. On databases where DDL commits the open transaction, such as Oracle, MySQL or H2, the question adds: "On this database, this statement also commits your pending changes."
Good to know

The confirmation is a safety net against mistakes, not access control. BroadSQL recognizes statement forms; it cannot see a function with side effects called from a SELECT, a trigger, a database link or the logic of a stored procedure. The database account's permissions remain the security boundary: where you only read, use an account that can only read. The account decides what is possible; BroadSQL makes sure you meant it.

Passwords: two kinds, handled with care

BroadSQL deals with two very different kinds of password, and 5.5.0 treats each with more care. It helps to keep them apart:

The master password and the passwords of your connections The master password opens the Connections Definition File on your computer and is changed with SET MASTER PASSWORD. Inside the file, each connection keeps the password of its own database account. That password belongs to the remote database and is changed in the database itself, with SET PASSWORD on H2, Derby and HSQLDB, or with the database's own tools elsewhere. Master password yours, typed at login SET MASTER PASSWORD Connections Definition File encrypted file on your computer SALES_DEV: user + password SALES_QA: user + password SALES_PROD: user + password Remote database account owned by the database SET PASSWORD or its own tools opens signs in
The master password protects the file; each saved password belongs to a database account. Changing one never changes the other.

The master password, typed when BroadSQL starts, encrypts the Connections Definition File (CDF) that holds all your connection definitions. The passwords of your connections belong to accounts on your databases; the CDF keeps a copy so that BroadSQL can sign in for you.

Changing the master password, without risking the CDF

Changing the key of an encrypted file is an operation where nothing may go wrong halfway. Up to 5.4.5, SET MASTER PASSWORD encrypted the CDF again in place. In 5.5.0 it never touches the live file until a checked replacement is ready:

  1. Back upA copy goes to the backups folder next to the CDF.
  2. CopyA copy of the CDF is encrypted with the new password.
  3. VerifyThe copy must open with the new password and hold exactly the same data.
  4. SwapOnly then does the copy take the place of the CDF.
  5. Check againThe CDF in place is opened and checked once more.

If any step fails, the CDF stays as it was and the message says so. If BroadSQL is stopped in the middle (a closed window, a power cut), the next login finishes the change when you type the new password, or offers to undo it when you type the previous one.

The command also refuses, before changing anything, passwords that would make the file impossible to open again. A master password has 2 to 256 Latin-1 characters (letters, digits, symbols, accents such as é or ß); spaces, control characters and characters outside Latin-1, such as €, are refused, because the database engine could not derive the same key again when opening the file.

Try it yourself

Every new installation ships with the same, publicly documented master password, clipper8AD. It protects nothing. If you have not changed it yet, this is the moment:

SET MASTER PASSWORD;

BroadSQL asks for the current password, then for the new one twice. A long passphrase without spaces, or 12 or more random characters, is a good choice; the Security page explains why length matters more than anything else. The backup it makes is encrypted with the previous password: keep it as safely as the CDF, or delete it once you no longer need it.

Changing a database password, only where it can be proven

SET PASSWORD changes the password of the account you are connected as, in the database itself, and then in BroadSQL. In earlier releases it tried a generic approach on several kinds of database and updated BroadSQL's own copy first. In 5.5.0 it does less, and does it properly:

DatabaseWhat to do
H2, HSQLDB, Apache Derby (NATIVE authentication)SET PASSWORD; changes the password in the database, proves it with a new connection, and only then saves it in the CDF.
Oracle, PostgreSQL, MySQL, MariaDB, SQL Server, Db2, SQLite and the othersSET PASSWORD refuses before changing anything. Change the password with the database's own tools or your identity provider, then update BroadSQL's copy with EDIT CONNECTION or in CONFIG.

Every step is reported, including the uncomfortable ones: if the database accepted the new password but BroadSQL could not save it, you are told to save it with EDIT CONNECTION. Other saved connections using the same account are listed, so you can update them too.

H2 passwords containing a semicolon, a quote or a backslash now work, a refused login no longer shows part of the password, and passwords are never written to BroadSQL's messages, logs or command history.

The CDF stays private

Because the CDF is an H2 database, earlier releases let you connect to it like any other database, and BroadSQL even started there. In 5.5.0 the CDF is no longer an ordinary destination. No connection, test, export, linked table or monitoring file can reach it, under any name, path or copy. When you genuinely need SQL on it, there is a deliberate door, and it asks for the master password every time:

Console: CDF maintenance mode
BroadSQL> CONNECT $CDF;
CDF maintenance: enter the BroadSQL master password.
[Enter password]
WARNING: CDF MAINTENANCE MODE
You are connected directly to the BroadSQL configuration database. Direct changes can make
your configuration unusable. Use CONFIG and the connection commands for normal changes.
Back up the CDF before changing it. DISCONNECT leaves maintenance mode.
$CDF [MAINTENANCE]>

The CDF also records a schema version now: an older CDF is upgraded once at startup, after a verified backup in conf/backups, and a newer one is refused unchanged. And LOGOUT (or LOGOFF) locks BroadSQL properly: it closes every connection and window, forgets the decrypted configuration, and asks for the master password again.

Why this matters

None of this adds new encryption: the CDF is protected by H2's AES file encryption, as before. What changes is everything around it: operations that could damage the file are staged and verified, the file cannot be reached by accident, and stepping away from your desk has a command.

A Scripts Library you can find your way around

A Scripts Library starts with five Scripts you know by heart. A year later it has eighty, written by four people, some of them copies of each other, a few written for an Oracle system retired in the spring. The value is still there; finding it is the problem. 5.5.0 gives the library a map, a set of lenses and a health check.

The map: LIB TREE

LIB TREE shows the library as its folders, with the number of Scripts in each and every Script's description. Here is a small library built on the WORLD sample database that ships with BroadSQL:

Console: real output, WORLD sample
WORLD> LIB TREE ALL;
Scripts Library tree (every Database Group/environment, 6 Scripts):
admin/ (2)
  cleanup.bsql         [deprecated] Removes test rows
  oracle_sessions.sql  Open sessions
cities/ (1)
  capitals.sql         [draft] Capital of each country
countries/ (3)
  by-codes.sql         Countries by ISO code
  copy-of-largest.sql  A copy someone made
  largest.sql          Largest countries of a continent

Without ALL, the tree follows the same scope as LIB LIST: the Scripts meant for the Database Group and Environment of your current connection. LIB TREE countries shows one folder.

The lenses: BY

Folders are one way to organize Scripts; tags, status and target systems are others. Add BY to LIB LIST or LIB FIND and the same Scripts come back in groups, by TAG, GROUP, ENVIRONMENT, DIALECT, STATUS, ACCESS or FOLDER:

Console: real output, trimmed to two groups
WORLD> LIB LIST ALL BY ACCESS;
Scripts Library (every Database Group/environment), grouped by access:
read (5)
|-------------------------|-----|-----------|----------|----------------|
|File                     |Group|Environment|Status    |Modified        |
|-------------------------|-----|-----------|----------|----------------|
|admin/cleanup.bsql       |NONE |NONE       |deprecated|2026-10-17 10:41|
|admin/oracle_sessions.sql|NONE |NONE       |          |2026-10-17 10:41|
|countries/by-codes.sql   |NONE |NONE       |          |2026-10-17 10:41|
|countries/largest.sql    |NONE |NONE       |          |2026-10-17 10:41|
|support/cities-since.sql |NONE |NONE       |          |2026-10-17 10:45|
|-------------------------|-----|-----------|----------|----------------|

(no access declared) (2)
|-----------------------------|-----|-----------|------|----------------|
|File                         |Group|Environment|Status|Modified        |
|-----------------------------|-----|-----------|------|----------------|
|cities/capitals.sql          |NONE |NONE       |draft |2026-10-17 10:41|
|countries/copy-of-largest.sql|NONE |NONE       |      |2026-10-17 10:41|
|-----------------------------|-----|-----------|------|----------------|

A Script with two tags appears in both groups, and Scripts without a value come last, so "what has nobody tagged yet?" has an answer too. LIB FIND searches the full text of every Script and groups its results the same way.

Two new pieces of metadata: access and dialect

A Script's header can now say two more useful things about it:

admin/oracle_sessions.sql
-- @description: Open sessions
-- @dialect: Oracle
-- @access: read
SELECT SID, USERNAME, STATUS FROM V$SESSION;

@access: read says the Script is meant to change nothing; @access: write says it does. @dialect names the connection types it is written for, or ANY. When you run a Script on another type of connection, BroadSQL tells you before the database does:

Console: real output
WORLD> @admin/oracle_sessions.sql;
The script oracle_sessions.sql is written for Oracle; the current connection's type is H2
Good to know

Both are information written by the Script's author, not permissions. A dialect mismatch prints a warning and the Script still runs. @access: read never skips a Production confirmation and never relaxes any safeguard; LIB SHOW displays it as Access: read (declared, not verified), which is exactly what it is. What it does give you is something to check, which brings us to the health check.

The health check: LIB LINT

LIB LINT already reported leftover %1 parameters and unknown Environments. In 5.5.0 it also checks every @param, @access and @dialect line, reports a "read" Script that contains a statement that may write, and finds duplicate Scripts:

Console: real output
WORLD> LIB LINT;
LIB LINT found 2 issue(s):
- admin/cleanup.bsql: declared @access read, statement 1 is a DELETE
- same SQL apart from layout, comments and letter case: countries/copy-of-largest.sql, countries/largest.sql (their @access declarations differ)

The first line catches a header that promises something its SQL does not keep. The second found that copy-of-largest.sql, written in lowercase with other line breaks, is the same query as largest.sql. Duplicates are compared as text, and nothing is merged or deleted: compare the two with LIB SHOW, remove one with LIB DEL, and LIB UNDO brings it back.

The BroadSQL Editor keeps pace. Its Metadata tab shows the typed parameters and edits Access and Dialect; its search matches tags; a Recent view lists the 20 Scripts you most recently opened, saved or ran; and Edit > Replace... (Ctrl+H) opens a proper Find and Replace dialog, where Replace All is a single undo step.

Parameters that check themselves

BroadSQL 5.4.0 replaced %1 with named arguments: country='FR' instead of a bare FR in position two. That made Scripts readable. 5.5.0 makes them dependable, by letting a Script say what each parameter is.

countries/largest.sql
-- @description: Largest countries of a continent
-- @tags: geo
-- @access: read
-- @param continent TEXT "Continent, as in COUNTRY.CONTINENT"
-- @param min_population INTEGER = 50000000 "Smallest population listed"
SELECT CODE, NAME, POPULATION
  FROM COUNTRY
 WHERE CONTINENT = ${continent} AND POPULATION >= ${min_population}
 ORDER BY POPULATION DESC;

Each -- @param line gives a name, a type (TEXT, INTEGER, DECIMAL, BOOLEAN, DATE, TIMESTAMP or LIST OF one of them), an optional default after = and a description in double quotes. A parameter with a default is optional; one without is required. The Script documents itself, and LIB SHOW turns the header into a table anyone can read:

Console: real output
WORLD> LIB SHOW countries/largest.sql;
Content of Scripts Library script: countries/largest.sql
Parameters:
|--------------|-------|--------|--------|----------------------------------|
|Name          |Type   |Required|Default |Description                       |
|--------------|-------|--------|--------|----------------------------------|
|continent     |TEXT   |Yes     |        |Continent, as in COUNTRY.CONTINENT|
|min_population|INTEGER|No      |50000000|Smallest population listed        |
|--------------|-------|--------|--------|----------------------------------|

Access: read (declared, not verified)

Wrong values stop at the door

BroadSQL converts every argument to its declared type before the Script starts. A wrong value refuses the call, and a refused call runs no statement and changes no variable. Compare:

A call with mistakes

@countries/largest.sql continent='Europe' min_population='many';
ERROR: countries/largest.sql: argument min_population is declared INTEGER: 'many' is a text value
countries/largest.sql: FAILED (0 statements, 0 failed)

@countries/largest.sql min_population=1;
ERROR: countries/largest.sql requires argument continent (declared in @param): pass it as name=value, or define a variable of that name with LET before the call

The corrected call

@countries/largest.sql continent='Europe';
-- continent = 'Europe'
-- min_population = 50000000
|----|------------------|----------|
|CODE|NAME              |POPULATION|
|----|------------------|----------|
|RUS |Russian Federation|146934000 |
|DEU |Germany           |82164700  |
|GBR |United Kingdom    |59623400  |
|FRA |France            |59225700  |
|ITA |Italy             |57680000  |
|UKR |Ukraine           |50456000  |
|----|------------------|----------|
countries/largest.sql: SUCCESS (1 statement, 0 failed)

Real output on the WORLD sample; run identifiers and the NAME column's padding are trimmed to fit.

Notice the lines starting with --: every value the Script used is shown, defaults included, so "what did you run exactly?" has its answer on the screen. Dates are strict too: a DATE takes 'yyyy-MM-dd' only, so since='17/10/2026' is refused with "is not a date (expected 'yyyy-MM-dd')" instead of being read as the wrong day.

Where a value comes from

Where a parameter's value comes from For each declared parameter BroadSQL uses the argument of the call, otherwise the session variable of the same name set with LET, otherwise the declared default. With none of them a required parameter refuses the call. The value is then checked against the declared type before the Script starts. 1. Argument continent='Asia' 2. Session variable LET continent = ... 3. Default = 50000000 Type check before any SQL Script runs none, or a wrong type: call refused, nothing runs
The first source that has a value wins, and that value is checked like any other. A missing argument is not a silent NULL.

The middle step is the convenient one. Set your context once with LET, and every Script that declares a parameter of the same name picks it up:

Console: real output, trimmed
WORLD> LET continent = 'Asia';
WORLD> LET min_population = 100000000;
WORLD> @countries/largest.sql;
-- continent = 'Asia'
-- min_population = 100000000
(6 rows: CHN, IND, IDN, PAK, BGD, JPN)
WORLD> @countries/largest.sql continent='Europe';
-- continent = 'Europe'
-- min_population = 100000000
(1 row: RUS)

The argument wins for that call only. Afterwards, continent is still 'Asia': in 5.5.0 the arguments of a Script call last exactly as long as the call, and every variable of the same name gets its previous value back when the Script returns. And a session variable of the wrong type is refused, not replaced by the default, with a message that tells you which variable to fix.

Lists, at last

Ask a support team for their most common query and it often looks like WHERE ID IN (...) with a handful of values pasted from a ticket. A LIST OF parameter is built for exactly that:

countries/by-codes.sql
-- @param codes LIST OF TEXT "Three-letter country codes"
SELECT CODE, NAME, CONTINENT FROM COUNTRY WHERE CODE IN (${codes}) ORDER BY CODE;
Console: real output
WORLD> @countries/by-codes.sql codes=('FRA', 'DEU', 'ITA');
-- codes = ('FRA', 'DEU', 'ITA')
|----|-------|---------|
|CODE|NAME   |CONTINENT|
|----|-------|---------|
|DEU |Germany|Europe   |
|FRA |France |Europe   |
|ITA |Italy  |Europe   |
|----|-------|---------|
countries/by-codes.sql: SUCCESS (1 statement, 0 failed)

WORLD> @countries/by-codes.sql codes='FRA';
ERROR: countries/by-codes.sql: argument codes is declared LIST OF TEXT: a single value was given; write a list, for example codes=('FRA')

Each element is sent as its own typed parameter: IN (${codes}) becomes IN (?, ?, ?), so a value never becomes SQL text. A list holds 1 to 1000 values, an empty list is refused, and ${codes} must stand alone between parentheses.

Good to know: who writes the parentheses?

Command line

You write the list with its parentheses, as a value: codes=('FRA', 'DEU'). In the Script, you write the parentheses of the SQL: IN (${codes}).

Editor's Run dialog

You type only the values, separated by commas: 'FRA', 'DEU'. A TEXT field takes its text without quotes: Europe, or O'Brien.

The same checks in the Editor

When you click Run in the BroadSQL Editor on a Script that declares parameters, the Run Parameters dialog opens with one field per parameter. Each field starts with the value the Script would use without it, the session variable or the default, so what you see is what runs. Run stays enabled; the values are checked when you click it, with the same rules as the command line, and every problem is listed at once:

Editor: Invalid parameters dialog (text)
Cannot run script.

The following parameters are invalid:

- continent: a value is required.
- min_population: expected an integer.

OK returns to the dialog with your values intact: one round trip instead of one error per attempt.

Settings without the text editor

BroadSQL's settings live in conf/BroadSQL.ini. Editing it by hand works, but it has traps everyone falls into once: a backslash written twice, a key in the wrong case, and no way to tell which value is actually in use. On Windows, CONFIG now has an Application Settings tab that removes those traps.

By hand

; was it one backslash or two?
DefaultFolder=C:\\BroadSQL\\exports
; is "autocommit" the right case?
autocommit=false

Then restart and hope. A key in the wrong case is not a BroadSQL setting at all.

In CONFIG

Type C:\BroadSQL\exports as it is; BroadSQL writes the escaped form. Each setting shows what it does, its accepted values, its default, the value in the file and the value in use. A key that only differs by case is listed with a suggestion: autocommit is not Autocommit.

  1. OpenType CONFIG;, from any prompt, and open Application Settings.
  2. SelectRead what the setting does and which value is in effect.
  3. ApplyChange the value; an invalid one is refused right there.
  4. SaveOnly your edited values change; the previous file is kept as BroadSQL.ini.bak.

The Status column answers the question you used to answer by restarting: Default (the file does not set it), In effect, Restart needed (the file holds a value that applies at the next start), Invalid in file (refused, the default is used until you fix it) and Read-only. Saving keeps your comments and the order of your lines exactly as they were, and if another program changed the file since the tab read it, nothing is written until you click Reload and check.

What happens if...?
...the folder I typed does not exist?
When you save, BroadSQL names the missing folder and asks. Create the folder creates it, then saves; Cancel saves nothing and keeps your edit. If it cannot be created, you see why, and nothing is saved.

Only ProductionConfirmEveryChange applies at once; every other change takes effect at the next start, and the panel says so. Paths behave as you would expect: a final separator is optional, and a relative path is relative to the installation folder (the two history settings excepted, which stay in your user profile). On Linux, where CONFIG is not available, edit the file; the Application settings page documents every key.

Knowing where you are

Being on the wrong connection is a classic fear of anyone who supports several systems. Several small changes in 5.5.0 make where you are, and how you got there, unmistakable.

SituationUp to 5.4.5Since 5.5.0
After the master passwordConnected to the CDF, at $CDF>At BroadSQL>, connected to nothing, or at your last connection if you ask for it
BYE / DISCONNECTBack to $CDF>Back to BroadSQL>, or to your previous connection
CONFIGOnly at $CDF>From any prompt
LOGOUTAsked for the password and kept the connection openCloses every connection and window first

BroadSQL> is a deliberately neutral place. Help, the Scripts Library, CONFIG and variables work there, but SQL is refused with "No active database connection. Use CONNECT <connection> first." A statement typed before you have chosen a database cannot land anywhere.

If you liked landing straight on a database, add RememberLastConnection=TRUE to the INI file. BroadSQL then reopens the connection you were on when you last closed it, and DISCONNECT steps back through the connections of your session, like a browser's Back button:

Console: RememberLastConnection=TRUE (fictional SALES connections)
BroadSQL> CONNECT SALES_DEV;
SALES_DEV> ENV QA;
SALES_QA> BYE;
SALES_DEV> BYE;
BroadSQL>

When the restored connection belongs to a Production Environment, BroadSQL says so at startup ("WARNING: SALES_PROD belongs to the Production Environment PROD."). Only the connection's ID is remembered, never a password or a URL, and LOGOUT clears it. On a shared or sensitive workstation, leave the setting at its default, FALSE.

Why this matters

Three commands, three clear meanings: DISCONNECT closes the current connection, LOGOUT locks BroadSQL, EXIT ends it. And the command history keeps your last 1,000 commands, in order, across restarts; one unreadable line no longer hides all the others.

Exports you can trust, even big ones

DUMP became BroadSQL's one export command in 5.4.0. 5.5.0 makes it more informative, lighter on memory and better at keeping local copies up to date.

When did it finish, and how long did it take?

Every successful DUMP or PULL now ends with a second line giving the completion time, with its UTC offset, and the elapsed time. It is exactly what you want to paste into a ticket:

Console: real output (path shortened)
WORLD> DUMP (SELECT CODE, NAME, POPULATION FROM COUNTRY WHERE CONTINENT = 'Europe') TO europe AS CSV;
52 row(s) exported to ...\exports\europe.csv.
Completed: 2026-10-17 10:42:21 +02:00 (0:00:00).

It appears only once the file is really written, never after a failure or a cancellation, so its presence means something.

MERGE: keep a local copy current

A local H2 copy of remote data is fast and safe to work on; the hard part was keeping it current. MODE MERGE KEY(...) inserts new rows, updates changed ones and leaves the rest alone:

Console: real output, run twice
WORLD> DUMP COUNTRY TO WORKCOPY.COUNTRY AS H2 MODE MERGE KEY(CODE);
MERGE into WORKCOPY.COUNTRY (table created): 254 source row(s), 254 inserted, 0 updated, 0 unchanged.
Completed: 2026-10-17 10:42:22 +02:00 (0:00:00).
WORLD> DUMP COUNTRY TO WORKCOPY.COUNTRY AS H2 MODE MERGE KEY(CODE);
MERGE into WORKCOPY.COUNTRY: 254 source row(s), 0 inserted, 0 updated, 254 unchanged.
Completed: 2026-10-17 10:42:22 +02:00 (0:00:00).

It is all or nothing, in one transaction: a NULL or duplicate key, a value that cannot be converted or different columns write nothing. MERGE never deletes, reads the whole source at every run, and is for AS H2 only.

Big spreadsheets, small memory

Writing a new XLSX file, a new worksheet or a worksheet with MODE OVERWRITE now streams its rows instead of building the whole workbook in memory. Measured on our development machine during the release, with eight columns of data:

RowsUp to 5.4.5Since 5.5.0
100,000about 11 s with 1 GB of memory4.3 s with 256 MB
300,000about 31 s with 4 GBabout 11 s with 512 MB
1,048,575 (a full sheet)out of memory with 4 GBabout 35 s with 256 MB

The files are the same, compared cell by cell. Appending to an existing XLSX worksheet and the ODS format still work in memory, so for very large results, prefer CSV or a new XLSX file. Long exports print a progress line every 10 seconds, and Ctrl+C stops a DUMP even while the database is still running its query. A cancelled or failed export no longer leaves a .tmp file behind, and an existing file is replaced only at the very end.

Housekeeping that happens by itself

A new LogRetentionDays setting (90 by default) deletes BroadSQL's own activity and application log files older than that number of days at startup; 0 keeps everything. Leftover DUMP temporary files from a crash are removed at a later start. And every release on the Downloads page now has a SHA-256 checksum, shown in the table and in a .sha256 file next to the ZIP, so you can check that what you downloaded is exactly what was published:

PowerShell, or a Linux shell
Get-FileHash BroadSQL-5.5.0.zip -Algorithm SHA256
sha256sum -c BroadSQL-5.5.0.zip.sha256

Finally, HELP <command> and the command reference pages now share one structure (purpose, syntax, examples, details, related pages), and the Security page was rewritten from measured facts, including which files are not encrypted and what stays your responsibility.

Case study: a production question at 4:40 on a Friday

Let's go back to the ticket from the introduction and follow it through, with every 5.5.0 piece in its place.

Fictional setting: an application called SALES with three Connections in one Database Group, SALES_DEV, SALES_QA and SALES_PROD, the last one in an Environment PROD flagged Production, on PostgreSQL. Table names, values and results below are illustrative; the commands and the shape of every message are those of 5.5.0.

  1. Start in a neutral place

    BroadSQL starts at BroadSQL>. Nothing typed by reflex can reach a database before the engineer decides which one.

    BroadSQL> CONNECT SALES_PROD;
    SALES_PROD>
  2. Find the right Script

    The library has grown. A search grouped by tag finds the diagnostic Script the team wrote last quarter, and LIB SHOW says what it needs.

    SALES_PROD> LIB FIND orders BY TAG;
    SALES_PROD> LIB SHOW support/stuck-orders.sql;
    support/stuck-orders.sql
    -- @description: Orders stuck in a status, for a few customers
    -- @tags: support, orders
    -- @access: read
    -- @dialect: PostgreSQL
    -- @param customer_ids LIST OF INTEGER "Customer IDs from the ticket"
    -- @param since DATE "Orders created on or after this date"
    -- @param status TEXT = 'PROCESSING' "Status to look for"
    SELECT ID, CUSTOMER_ID, STATUS, CREATED_AT
      FROM ORDERS
     WHERE CUSTOMER_ID IN (${customer_ids})
       AND CREATED_AT >= ${since}
       AND STATUS = ${status}
     ORDER BY CREATED_AT;
  3. Supply validated parameters

    The ticket says "since 13/10". The first attempt is refused before any SQL runs; the second one runs, and shows the values it used.

    SALES_PROD> LIB RUN support/stuck-orders.sql customer_ids=(1042, 1077, 2210) since='13/10/2026';
    ERROR: support/stuck-orders.sql: argument since is declared DATE: '13/10/2026' is not a date (expected 'yyyy-MM-dd')
    SALES_PROD> LIB RUN support/stuck-orders.sql customer_ids=(1042, 1077, 2210) since='2026-10-13';
    -- customer_ids = (1042, 1077, 2210)
    -- since = 2026-10-13
    -- status = 'PROCESSING'
    (3 rows, one per customer, all created on Monday)
    support/stuck-orders.sql: SUCCESS (1 statement, 0 failed)
  4. Keep the evidence

    The result on screen is what goes into the ticket: DUMP / exports the displayed result without running the query again, and the completion line gives the time.

    SALES_PROD> DUMP / TO ticket_4471_stuck_orders AS CSV;
    3 row(s) exported to C:\BroadSQL\exports\ticket_4471_stuck_orders.csv.
    Completed: 2026-10-16 16:52:07 +02:00 (0:00:00).
  5. The reflex, caught

    Someone on the call asks for a quick reset. The engineer types it, and BroadSQL asks. On reflection, this is a change for the application team's procedure, not for a Friday afternoon console. The answer is no, and nothing reaches the database.

    SALES_PROD> UPDATE ORDERS SET STATUS = 'PENDING' WHERE CUSTOMER_ID IN (1042, 1077, 2210);
    WARNING: This operation can modify a Production database.
      Connection: SALES_PROD (Environment PROD, Production)
      Statement:  UPDATE ORDERS SET STATUS = 'PENDING' WHERE CUSTOMER_ID IN (1042, 1077, 2210)
      Why:        it is an UPDATE
    Execute? [yes/no]: no
    Not executed.
    Production change refused on connection 'SALES_PROD': the change was not confirmed. Nothing was executed.
  6. Reproduce safely, then lock up

    ENV QA moves to the QA connection of the same Database Group, where the same Script can run again with the same arguments, without any confirmation. Before leaving, LOGOUT closes every connection and locks BroadSQL until the master password is typed again.

    SALES_PROD> ENV QA;
    SALES_QA> LOGOUT;
    Logged out. Enter the master password to continue.

The outcome: a ticket with a CSV attachment, a timestamp and the exact parameters used, a reproduction path on QA, and no unplanned change on production. Each 5.5.0 improvement simply removed one way for the afternoon to go wrong.

Before you upgrade

5.5.0 needs no special installation steps, but a few behaviors changed on purpose:

  • Startup lands at BroadSQL>. Set RememberLastConnection=TRUE to reopen your last connection at startup.
  • Automation against Production. A job that runs BroadSQL with redirected input and modifies a database in a Production Environment will now be refused. Review such jobs before upgrading; no setting turns this off.
  • Your CDF is upgraded once. The first start makes a verified backup in conf/backups, then upgrades the CDF. An upgraded CDF cannot be used by an older release, so keep your previous installation folder until 5.5.0 has started once.
  • Old logs are cleaned up. Even without the key in your INI file, LogRetentionDays is 90: log files older than that are deleted at the first start. Add LogRetentionDays=0 first if you need to keep them.
  • SET PASSWORD works on H2, Derby and HSQLDB only. On other databases, change the password with the database's tools, then with EDIT CONNECTION.
  • Scripts with @param need 5.5.0. An older BroadSQL ignores @param lines, so a Script that relies on a default fails there.
  • Change the default master password if you have not done it yet: SET MASTER PASSWORD;

The Starting, connecting and logging out page and the CDF schema versions section cover the details.

Wrapping up

If 5.4.x made BroadSQL a nicer place to work, 5.5.0 makes it a place where you can relax a little. Production asks before it changes. Password changes check their own work. Scripts say what they need and refuse what they cannot use, and a growing library becomes something you can browse and audit. Settings explain themselves, the prompt tells you where you are, and exports tell you when they finished.

None of it asks you to work differently. It simply takes a few of the ways a normal day can go wrong and closes them, quietly, so that you can concentrate on the question in front of you.

New to BroadSQL? Start with Getting started and the WORLD sample database. Already using it? Install 5.5.0, run LIB LINT on your library, add a -- @param line to the Script you run most often, and flag your production Environments in CONFIG if you have not already. We think you will like the difference.

Found something wrong or unclear on this page? , with the page address.