Added
-
redb.Templatesis a collection of seven templates. Each one runs as is: SQLite with Pro, the PVT
prefilter and change tracking on, the props cache off with the lines that turn it on in a comment. Every
project that has a database also ships aDockerfileand compose files for PostgreSQL and SQL Server.redb: the console app, as before.redb-razor: a Razor Pages site - a product list with search, sorting and paging in the database, and a
create / edit form.redb-blazor: the same as a Blazor site (Interactive Server), plus a category tree. Components take an
IRedbServiceper operation (RedbWork), not one per circuit.redb-worker: an integration worker - a folder inbox, an XSD check, one.Transacted()write to redb, a
receipt in the outbox; a repeated order is answered as a duplicate through the object's unique key.redb-chat: an LLM chat on redb.Route.Llm with the history in redb, in the console and over HTTP.
--tools none|shell|mcpadds a read-only system-command tool or the tools of an MCP server;--audit
stamps the user and audit tags on every message and reads them back with a LINQ query. The API key goes
intoappsettings.json; a comment there shows how to switch to DeepSeek.redb-app: a Blazor WebAssembly client and a REST DSL API with sign-in (JWT throughInboundAuth=Bearer),
CORS for development and nginx on one origin for deployment.redb-bff: a Blazor Server backend-for-frontend with a cookie session, and a backend of redb.Route
controllers that only the web server may call, with a service key.
The route-based templates (
redb-worker,redb-chat, and the servers ofredb-appandredb-bff) are a
module withInitRoute.mainplus a host that calls it. The same module runs in its own host or on a Tsak
worker:deploy/pack-tpkg.ps1packs it,deploy/docker-compose.tsak.ymlruns it on the Tsak stack image,
and one set of setting keys and environment variables works in both places.
Changed
-
The
redbconsole template turns on the PVT prefilter and change tracking in its Pro variant; they
were commented out. The props cache stays off, with the lines that turn it on next to it. -
SQL Server Pro: string matching follows the contract of COLLATION.md, as SQL Server Free does. Plain
Contains/StartsWith/EndsWithwere forced case-sensitive (COLLATE Latin1_General_CS_AS) whatever the
column said; they now keep the column collation - case-insensitive on the default one. The*IgnoreCaseforms
were forced toLatin1_General_CI_AI, which also ignored accents (mullerfoundMüller); they now fold case
only, withLOWER. On the same database Free and Pro answered the same query differently; they agree now.
Code that relied on Pro's case-sensitive plain match on a case-insensitive database should say so with an
ordinal re-check in memory, as COLLATION.md describes. The prefilter renderer changed with it, so it stays a
superset of the filter. -
Configuration copying has one list of settings.
RedbServiceConfiguration.CopyFromcopies every setting,
CopyBehaviourFromevery setting except the connection identity (ConnectionString,CacheDomain);Clone,
ApplyTemporaryand the validator's auto-fix go through them. TheCloneextension method in
RedbServiceConfigurationExtensionsis removed - the instance method always won over it, so only an explicit
static call reached it. -
The obsolete
InitializeAsync/AutoSyncSchemesAsyncextensions delegate toIRedbService.InitializeAsync.
They carried their own copy of the start-up sequence: schemes synchronized in parallel on one service instance,
no up-front validation of scheme names, and every synchronization error swallowed. -
SQL Server (Free): the filter and aggregate builders refuse what they cannot read. An operator they did
not know became1=1(the condition vanished) or1=0(no rows - and every row under$not); an unknown
comparison became=; a value that was not a number became 0; an aggregate they could not compile became a
NULL column. They now fail through one helper,dbo.pvt_fail, with a message that starts with "redb:", where
the PostgreSQL module raises. LINQ does not produce these shapes; JSON filters sent directly do. -
The SQL modules' smoke runners run in the integration suite (
PostgresModuleSmokeTests,
MsSqlModuleSmokeTests). They were run by hand only. -
PostgreSQL Pro: the pivot takes a scalar field with
max()instead of(array_agg(...))[1]. The array
per object and per field kept the planner on a sorted GroupAggregate;max()lets it hash-aggregate. A
filtered, sorted page over 200 000 objects went from 1.1-1.4 s to 0.84-0.98 s. The FILTER leaves one row per
object, so the value is the same. PostgreSQL 14 has nomax()for every type:boolfields takebool_or,
Guidandbyte[]fields keeparray_agg. One helper (PvtPick) now renders this for every Pro PostgreSQL
query shape (flat, tree, aggregate, grouping, window, grouped window, array grouping) instead of 26 copies.
Fixed
- Pro (all three providers):
ToAggregateSqlStringAsyncshowed the aggregate without the query'sWhere.
The preview built the SQL with no filter whileAggregateAsyncruns with it, so the preview of a filtered sum
was a sum over the whole scheme. It now passes the same filter. Only the preview was wrong; results were not. - SQL Server (Free), JSON filters, found by the smoke runner once it ran:
$arrayAtread its operand as the
index and never compared the value - it now takes{"index":N,"value":V}as PostgreSQL does;$arrayCountLte
was spelled$arraycountleand matched nothing;$ilikeon a field was missing;{"Dict.ContainsKey":"key"}
with a bare string key was never recognised. Each returned no rows without an error. SQL Server module
version 0.2.26. - Configuration copies lost five settings.
Clonedid not copyAutoApplyDatabaseUpgrades,StringCollation,
EnablePvtPrefilter,PropsSaveStrategyandDefaultCheckPermissionsOnQuery. The auto-fix of an invalid
configuration (GetValidatedRedbServiceConfigurationwithout throwing) returned such a copy, so it could switch
automatic schema upgrades back on for an operator who had turned them off. ApplyTemporaryleft its change behind. The builder form edited the live configuration before taking the
snapshot meant to undo it, so the scope ended with the temporary values in place. The configuration form
applied and restored fourteen settings out of thirty-six. Both apply and restore every behaviour setting now,
and never touch the connection string or the cache domain.- Pro: a stored collection element that did not convert to the element type was lost without a word. The
same failure had three outcomes, chosen by the shape of the collection: a dictionary entry vanished, an array
element became the type's default (0), a list element was skipped. Loading now fails with the value, the target
type and the original error. - SQLite: a column value the row mapper could not convert left the property at its default. Two silent
catchblocks made the row read as if the column were NULL. It fails now, naming the column, the value and the
property. Conversions use the invariant culture (a decimal text like1.5was parsed with the current one). - Tree projections (
TreeQueryableBase.Select):Take/Skip/DistinctafterWhere/OrderBy.
WhereandOrderByran in memory after the projection whileTakeandSkipwent to SQL before it, so the
page was cut from the unfiltered, unsorted nodes;Distinctwent to the source query, where every node is
distinct. They now run in call order after the projection, as the flat projection has done since S-4. - Free:
DistinctByRedb/DistinctByon a tree query returned every node. The flat query passed the
distinct key topvt_build_query_sql; the three Free tree providers passed none. They pass it now. The SQL
builders then had to honour it in the tree shape too: SQL Server ignored the key in the tree branch without
props fields, and the SQLite native extension ignored it in the wide shape (a tree without props fields, or a
filter with an absence check). SQL Server module version 0.2.24, SQLite extension version 0.6.7 - the SQLite
native library is rebuilt for Windows x64 and Linux x64/arm64. - Pro:
DistinctByRedb/DistinctByon a tree query.- SQLite Pro ignored the distinct key on a tree and returned every node: the key was computed and never put
into the SQL. The tree now ranks rows withROW_NUMBER()as the flat query does. CountAsyncof a distinct tree query was wrong on all three Pro providers. PostgreSQL counted every node
(COUNT(DISTINCT o._id)- the ids are distinct whatever the key is); SQL Server and SQLite looked for a
PostgreSQLDISTINCT ON, did not find it, and returned the first column of the first row - an object id.
The count is now the number of distinct keys, and a count that cannot find the distinct query refuses.
- SQLite Pro ignored the distinct key on a tree and returned every node: the key was computed and never put
- SQL Server (Free): an unknown nested field resolved to its parent. T-SQL keeps a variable's old value when
SELECT @v = ...finds no row, sodbo.pvt_resolve_field_pathreturned the parent structure for a child the
scheme does not have (Contacts[].NoSuchFieldbecame theContactsarray), and a filter, sort or grouping on
that path read the wrong structure. The dictionary child and the list-item accessor had the same trap. - SQL Server (Free): the array grouping skipped a key or aggregate field the item does not have. The grouping
ran on the remaining keys, the aggregate vanished from the result, and with no key left the flat list of items
came back instead of groups. Such a request is refused now, as is an unknown aggregate function. - Pro: HAVING constants. An array or object in
$constwas bound as its raw JSON text and compared as a
string; it is refused now. The unreachable fallback operator=of the translator is a refusal too. - SQL Server module version 0.2.26: the functions are reinstalled on the first start.