diff --git a/CHANGELOG.md b/CHANGELOG.md index 9929b546..09be1bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,27 @@ ### Features +* **SDBQL gains set operations, recursive CTEs, `RETURN DISTINCT`, `COLLECT + … KEEP`, the `NONE` quantifier, and standalone `OFFSET`.** Query blocks can + be combined with `UNION [ALL]`, `INTERSECT`, and `EXCEPT` (`FOR … RETURN … + UNION ALL FOR … RETURN …`; either side may be parenthesized; duplicates are + removed except for `UNION ALL`). Chains follow SQL precedence — `INTERSECT` + binds tighter than `UNION`/`EXCEPT`, which chain left to right — and rows + are compared by value, the same equality `UNION()`/`INTERSECTION()` use. + `WITH RECURSIVE name AS ( UNION ALL )` iterates the step until + it stops producing rows — inside the step the CTE name is bound to the rows + of the *previous* iteration (hierarchies, org charts, transitive closures), + with safety caps at 1,000 iterations / 1M rows. `RETURN DISTINCT expr` + deduplicates result rows (first occurrence wins). `COLLECT … INTO g KEEP v1, + v2` restricts which in-scope variables are stored in the group arrays (an + unknown name is an error). `NONE(x IN arr SATISFIES cond)` — and the + function form `NONE(arr, x -> cond)` — is true when no element satisfies. + `OFFSET n` works alone or as `LIMIT n OFFSET m`. A CTE declared before a set + operation binds in every operand, and operands, CTE bodies and subqueries + are all executed as full query blocks — their own `WITH`, pre-`FOR` `LET`s, + `SORT`/`LIMIT` and nested set operations all apply. Permission checks, query + caching, cache invalidation, the long-running-query gate and + `has_mutations()` all see through set-operation operands and CTE bodies. * **Query-driven auto-indexes (opt-in).** Collections with `autoIndex: true` (or unset + `SOLIDB_AUTO_INDEX=1`) create a persistent `_auto_{field}` index the first time a `FOR` + `FILTER` equality/range miss would have used diff --git a/admin/app/views/query/_explain.html.slv b/admin/app/views/query/_explain.html.slv index 53e5c604..2fa0ca52 100644 --- a/admin/app/views/query/_explain.html.slv +++ b/admin/app/views/query/_explain.html.slv @@ -97,8 +97,13 @@ <% end %> <% if !limit_info.nil? %> + <%# A standalone OFFSET reports no count %> + <% limit_offset = limit_info["offset"] ?? 0 %> + <% limit_count = limit_info["count"] %> + <% limit_text = limit_count.nil? ? "offset " + str(limit_offset) : "limit " + str(limit_count) %> + <% limit_text = limit_text + " offset " + str(limit_offset) if !limit_count.nil? && limit_offset > 0 %> - limit <%= limit_info["count"] ?? 0 %><% if (limit_info["offset"] ?? 0) > 0 %> offset <%= limit_info["offset"] %><% end %> + <%= limit_text %> <% end %> diff --git a/doc/app/views/docs/changelog.html.slv b/doc/app/views/docs/changelog.html.slv index 3a1fe36b..7bd3ab52 100644 --- a/doc/app/views/docs/changelog.html.slv +++ b/doc/app/views/docs/changelog.html.slv @@ -17,6 +17,7 @@ Security hardening, SDBQL function completeness, and one product breaking change: the client-facing job and cron queue is removed. Application background jobs now live in the Soli framework. Triggers keep working, on the same machinery as before. Also breaking if you relied on them: default bind is loopback, replication TCP requires a keyfile, LENGTH on a string is character count (not a collection count), installing Lua requires Admin, and API keys must declare a role.