The upgrade-check command checks a 3.3 datastore before it moves to 4.0. It lists the stored definitions and records that 4.0 refuses, fails on or treats differently, each with a severity, the effect on 4.0, a fix and a link to the upgrade guide. The check only reads, and never runs a stored definition, so it is safe to run against a production server.
The command belongs to the 3.3 release line of the database and is not part of 4.0, because the check has to run while the datastore can still go back to 3.3. Upgrade to the latest 3.3 release to get it. If the default database component is already 4.0, run the 3.3 one with +3, as in surreal +3 db upgrade-check. See Version names.
surreal db upgrade-check [OPTIONS]Example usage
To check every namespace of a running server, sign in as a root user:
surreal db upgrade-check --endpoint http://localhost:8000 --username root --password secretEach finding gives its location, the reason, the rule that found it, what 4.0 does, the fix and a link. The last line gives the verdict and the counts. The report below comes from a database with a view whose FROM names its own table and an index on a datetime field:
[BLOCKING] shop/main table agg: view agg reads FROM its own table (view-reads-own-table)
4.0: Every write to a table the view reads fails with `Cannot maintain the view: it reads itself`.
fix: Define the view again with DEFINE TABLE OVERWRITE over the tables other than its own, or remove it.
docs: https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#views-that-read-their-own-table
[warning] shop/main table ev ev_at: index keys at, which is typed datetime (index-on-datetime)
4.0: An index built by 3.x keeps the text order of datetimes, in which a whole second sorts after its fractions, so ORDER BY and ranges over the field can return records out of order or leave some out.
fix: Run REBUILD INDEX on this index once every node runs 4.0.
docs: https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#datetimes-in-an-index
Not detectable from stored state:
- 4.0 refuses CBOR tags 0, 9 and 13 (datetimes, UUIDs and durations as text), which some SDK releases send. Upgrade every SDK to a release that supports 4.0 before upgrading the server. (https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#cbor-text-forms-of-datetimes-uuids-and-durations)
- The 3.x Rust SDK, the SDKs built on it and JavaScript SDK releases before 2.1 refuse a 4.0 server. The 4.0 SDKs connect to 3.x servers as well, so upgrade the SDKs first. (https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#sdk-version-checks)
- Schema scripts, not stored definitions: 4.0 refuses a clause repeated in DEFINE TABLE, a DEFINE INDEX with two kinds, and a dec literal with more than 28 decimal places. The stored form of each is unaffected. (https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#a-clause-repeated-in-define-table)
- Queries sent by applications are not stored, so the check cannot see them. Read the behaviour changes, such as results without ORDER BY and float to decimal comparisons. (https://surrealdb.com/docs/build/migrating/from-old-surrealdb-versions/3x-to-4x#behaviour-changes)
Verdict: blocking. 1 namespace(s), 1 database(s), 5 definition(s): 1 blocking, 1 warning(s), 0 info, 0 unreadable scope(s). Checked by 3.3.6.The notes at the end are in every report. They name changes that the stored state cannot show, such as the queries an application sends.
To check one database with a database user, and to read up to 500 records of each table as well:
surreal db upgrade-check -e http://localhost:8000 -u app -p hunter123 --auth-level database --ns shop --db main --scan-records 500To check a datastore with no server running, give its path. The command opens the datastore itself and runs the check as the root owner, so stop the server first:
surreal db upgrade-check --endpoint surrealkv://path/to/dataHow the check runs
The command reaches the same rules in three ways:
| Endpoint | How the check runs |
|---|---|
http://, https://, ws:// or wss:// | The command calls the server's /upgrade-check route with your credentials, and the server reads its own catalog. |
A server without the /upgrade-check route, or --client-side | A server from an earlier 3.3 release answers the route with 404, and the command then reads each definition with INFO statements, and records with SELECT, and runs the check itself. The report's checked_by ends with (client-side, through INFO). |
A local path, such as surrealkv:// or rocksdb:// | The command opens the datastore in its own process. |
Over a gRPC endpoint, the command always uses INFO statements, because the route is served over HTTP only.
The check covers the scope of the user who signs in. A root user sees every namespace, and the report names the storage engine only for a root user. A namespace or database user sees only its own namespace or database, and naming another one with --namespace or --database is refused. A record user is refused.
Severities and exit status
Every finding has one of three severities:
| Severity | Meaning |
|---|---|
blocking | 4.0 refuses or fails on this definition or record, so change it before the upgrade. |
warning | 4.0 keeps working, but gives different results or needs an action, such as REBUILD INDEX, a change to a schema script, or room on the disk. |
info | No action is needed. |
The verdict is blocking when there is a blocking finding, needs changes (needs_changes in JSON) when there is a warning, and compatible otherwise. The command exits with a non-zero status when the verdict is blocking, or when it could not read a namespace, a database or a table, so a script can stop before the upgrade:
1 blocking issue(s) found; resolve them before upgrading to 4.0The rules, and what each finds, are listed under Checking a datastore before the upgrade.
Record scan
Most rules read definitions only. Two rules read records, record-table-value and record-not-object, so they run only with --scan-records. The server limits the scan to 1,000,000 records of each table, 10,000,000 records in all and 600 seconds, whatever the options ask for. When a limit stops the scan, the summary line says (scan cut short by its budget), and the JSON report has "complete": false.