More control, fewer surprises: what BroadSQL 5.5.0 brings you
October 17, 2026
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
LIB TREE, grouping with BY, duplicate detection and declared access and dialect.
4Parameters that check themselvesTyped, optional and defaulted parameters, and real list parameters for IN (...).
5Settings in CONFIGAn Application Settings tab that explains, validates and saves every setting of the INI file.
6Clearer sessions and exportsA neutral startup prompt, LOGOUT, completion times, MERGE into local tables and lighter XLSX exports.
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:
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:
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.
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 asks | Always asks |
|---|---|
SELECT, WITH queries, SHOW, DESCRIBE, EXPLAIN | INSERT, UPDATE, DELETE, MERGE, every DDL statement |
Session statements such as SET SCHEMA or USE | GRANT, REVOKE, other SET statements |
COMMIT, ROLLBACK, SAVEPOINT | CALL, EXEC, BEGIN blocks: BroadSQL cannot see inside them |
| BroadSQL commands that only read | SELECT ... 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.
- ...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."
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, 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:
- Back upA copy goes to the
backupsfolder next to the CDF. - CopyA copy of the CDF is encrypted with the new password.
- VerifyThe copy must open with the new password and hold exactly the same data.
- SwapOnly then does the copy take the place of the CDF.
- 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.
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:
| Database | What 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 others | SET 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:
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.
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:
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 continentWithout 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:
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:
-- @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:
WORLD> @admin/oracle_sessions.sql;
The script oracle_sessions.sql is written for Oracle; the current connection's type is H2Both 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:
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.
-- @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:
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 callThe 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
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:
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:
-- @param codes LIST OF TEXT "Three-letter country codes"
SELECT CODE, NAME, CONTINENT FROM COUNTRY WHERE CODE IN (${codes}) ORDER BY CODE;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.
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:
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.
- OpenType
CONFIG;, from any prompt, and open Application Settings. - SelectRead what the setting does and which value is in effect.
- ApplyChange the value; an invalid one is refused right there.
- 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.
- ...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.
| Situation | Up to 5.4.5 | Since 5.5.0 |
|---|---|---|
| After the master password | Connected to the CDF, at $CDF> | At BroadSQL>, connected to nothing, or at your last connection if you ask for it |
BYE / DISCONNECT | Back to $CDF> | Back to BroadSQL>, or to your previous connection |
CONFIG | Only at $CDF> | From any prompt |
LOGOUT | Asked for the password and kept the connection open | Closes 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:
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.
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:
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:
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:
| Rows | Up to 5.4.5 | Since 5.5.0 |
|---|---|---|
| 100,000 | about 11 s with 1 GB of memory | 4.3 s with 256 MB |
| 300,000 | about 31 s with 4 GB | about 11 s with 512 MB |
| 1,048,575 (a full sheet) | out of memory with 4 GB | about 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:
Get-FileHash BroadSQL-5.5.0.zip -Algorithm SHA256
sha256sum -c BroadSQL-5.5.0.zip.sha256Finally, 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.
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>Find the right Script
The library has grown. A search grouped by tag finds the diagnostic Script the team wrote last quarter, and
LIB SHOWsays 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;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)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).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.Reproduce safely, then lock up
ENV QAmoves 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,LOGOUTcloses 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>. SetRememberLastConnection=TRUEto 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,
LogRetentionDaysis 90: log files older than that are deleted at the first start. AddLogRetentionDays=0first if you need to keep them. SET PASSWORDworks on H2, Derby and HSQLDB only. On other databases, change the password with the database's tools, then withEDIT CONNECTION.- Scripts with
@paramneed 5.5.0. An older BroadSQL ignores@paramlines, 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? Tell us, with the page address.