Breaking Changes between Lucee 7.0 and 7.1
Breaking Changes between Lucee 7.0 and 7.1
This document outlines the breaking changes introduced when upgrading from Lucee 7.0 to Lucee 7.1.
Be aware of these changes when migrating your applications to ensure smooth compatibility.
Other Breaking Changes in Lucee Releases
- Breaking Changes between Lucee 5.4 and 6.0
- Breaking Changes Between Lucee 6.0 and 6.1
- Breaking Changes between Lucee 6.1 and 6.2
- Breaking Changes between Lucee 6.2 and 7.0
Struct Key Ordering Changes
The internal ordering of keys in unordered (non-linked) structs has changed in Lucee 7.1. This means that serializeJSON() output for regular structs may produce keys in a different order than previous versions.
Any code that relied on unordered structs having a consistent key order may break (e.g. comparing serialized JSON strings, iterating keys, or generating cached markup).
Example:
var s = { "data": {}, "includes": ["a.js"], "adhoc": {} };
// 7.0: {"data":{},"includes":["a.js"],"adhoc":{}}
// 7.1: {"includes":["a.js"],"adhoc":{},"data":{}}
Workarounds:
- Use
StructNew("ordered")orStructNew("linked")if key order matters - Compare deserialized structs instead of raw JSON strings
- Use
structSort()or sorted keys when generating deterministic output - As a diagnostic, the
lucee.concurrent.map.impl=legacyflag temporarily restores pre-7.1 ordering — flip it on to confirm whether breakage is genuinely an ordering-assumption bug in your code. See Diagnosing struct iteration-order regressions on 7.1 with `lucee.concurrent.map.impl=legacy`.
This change was made to improve struct performance (LDEV-5098).
QoQ SQL String Concatenation with NULL values
Previously, the HSQLDB-backed Query of Queries engine propagated NULL during string concatenation (like MySQL's CONCAT()) — 'foo' || NULL would return NULL. This caused rows with NULL values to be silently excluded from query results when using || or CONCAT() in WHERE clauses.
The native QoQ engine, which previously didn't support || or N-arg CONCAT() and fell back to HSQLDB.
Lucee 7.1 now treats NULL as an empty string during string concatenation in QoQ, matching Adobe ColdFusion behaviour, Oracle's || operator and SQL Server's CONCAT() function.
Examples using || operator:
-- Given a query with four rows: "", "123", NULL, "456"
SELECT name FROM qry WHERE CONCAT( ',', name, ',' ) NOT LIKE '%,123,%'
-- or
SELECT name FROM qry WHERE ',' || name || ',' NOT LIKE '%,123,%'
-- 7.0: returns 1 row (456) — ',' || NULL || ',' returns NULL, and NULL NOT LIKE '%..%' is NULL, not TRUE
-- 7.1: returns 3 rows ("", NULL, "456") — NULL is treated as empty string
Both || and CONCAT() are functionally identical — they now treat NULL as an empty string in both the native and HSQLDB engines.
If your code relies on NULL propagation in QoQ string concatenation, you may need to update your queries.
QoQ HSQLDB ORDER BY null position now matches native engine
The HSQLDB engine in Query of Queries sorted nulls first regardless of ASC or DESC direction, diverging from native QoQ and Adobe ColdFusion (which treat nulls as a low value: first ASC, last DESC). Lucee 7.1 aligns the engines.
Opt out of the new behaviour with lucee.qoq.hsqldb.orderBy.nullsLastInDesc=false (or LUCEE_QOQ_HSQLDB_ORDERBY_NULLSLASTINDESC=false).
REST request routing now uses specificity scoring
Cross-CFC REST dispatch in 7.0 returned the first match in disk-iteration order, which varied by filesystem. Lucee 7.1 picks the most-specific match (literal > regex-constrained > unconstrained path-var, longer > shorter, with deterministic tie-breaking) — matching JAX-RS rules. See REST Services with Lucee for the full rules.
Three knock-on effects to watch for:
- Routes that 404'd because the wrong CFC won may now return 200 — audit
overlapping
restPathvalues, especially handlers that have been dead for years. - Two CFCs in the same mapping declaring the same
restPathfail at registration with anApplicationExceptionnaming both files. Pre-7.1 silently shadowed one. - Filename-ordering tricks (e.g.
aDefault.cfcto exploit alphabetical iteration) no longer work — filenames are only used as a final tie-break.