Skip to content

Commit c9dc162

Browse files
Merge pull request #479 from MPCoreDeveloper/release/2.1.2
Release 2.1.2 - bound-parameter literal scanner, docs and version bump
2 parents 9faad61 + 1787c15 commit c9dc162

81 files changed

Lines changed: 1357 additions & 503 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Directory.Packages.props‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
5-
<SharpCoreDBVersion>2.1.1</SharpCoreDBVersion>
5+
<SharpCoreDBVersion>2.1.2</SharpCoreDBVersion>
66
</PropertyGroup>
77
<ItemGroup>
88
<PackageVersion Include="SharpCoreDB" Version="$(SharpCoreDBVersion)" />

‎README.md‎

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ Use it when you need:
2828
measured 2026-10-11)**, the **RC.3 sweep of 2,897 tests across 16 suites**, and **backward
2929
compatibility**
3030

31-
> **Current release: v2.1.0 (2026-10-10) — the .NET 11 / C# 15 line, on `master`.** **Published on
31+
> **Current release: v2.1.2 (2026-10-26) — the .NET 11 / C# 15 line, on `master`.** A data-correctness
32+
> patch with no API changes: a **bound string parameter holding an apostrophe, a doubled quote or a
33+
> backslash was re-scanned as SQL**, so the value could land in the neighbouring column, and a value
34+
> ending in a backslash could not be written at all. One scanner now owns SQL's and SQLite's escaping
35+
> rules for every literal reader on the statement path. Validated by **core 2,107 tests / 0 failed,
36+
> VectorSearch 255 / 0, EF Core 116 / 0, linq2DB 24 / 0**, and a two-file runnable reproduction ships in
37+
> [`docs/sharpcoredb-known-issues.md`](docs/sharpcoredb-known-issues.md) §7. Full notes:
38+
> [`docs/CHANGELOG.md`](docs/CHANGELOG.md) › `[2.1.2]`.
39+
40+
> **Previous release — v2.1.0 (2026-10-10) — the .NET 11 / C# 15 line, on `master`.** **Published on
3241
> NuGet.org** (all 27 packable packages) from `master`, `net11.0` only. Highlights: the fixed-width inline capacity
3342
> ships at 24 bytes by default on both storage paths (worth **+34 %** on the batched multi-row INSERT
3443
> shape and **0,63× → 0,87×** on the tracked fair-PK INSERT ratio), the overflow arena appends a row's payloads in one call instead of one
@@ -73,17 +82,17 @@ Use it when you need:
7382
7483
## Requirements and the two release lines
7584

76-
| | **v2.1.0 — this line (`master`)** | **v2.0.0.3 — parked line (`release/v2.0`)** |
85+
| | **v2.1.2 — this line (`master`)** | **v2.0.0.3 — parked line (`release/v2.0`)** |
7786
|---|---|---|
7887
| Target framework | `net11.0` only (every project under `src`) | `net10.0` |
7988
| Language | C# 15 (`LangVersion=preview`, always on) | C# 14 |
8089
| SDK | .NET 11 RC, pinned in `global.json` (`11.0.100-rc.1.26425.128`, `allowPrerelease`, `rollForward: latestPatch`) | .NET 10 (`10.0.x`) |
8190
| Pipeline | `.github/workflows/ci.yml` | `.github/workflows/ci-net10.yml` |
82-
| Packages | `2.1.0` — the 27 packable projects under `src` | `2.0.0.3` |
91+
| Packages | `2.1.2` — the 27 packable projects under `src` | `2.0.0.3` |
8392
| Status | current release, receives new work | servicing only |
8493

8594
- **Pick one line per solution.** The package references are line-specific: a `net10.0` project uses the
86-
`2.0.0.x` packages, a `net11.0` project the `2.1.0` ones. Do not mix them in one solution.
95+
`2.0.0.x` packages, a `net11.0` project the `2.1.2` ones. Do not mix them in one solution.
8796
- **The .NET 11 SDK is required to build** — the `global.json` pin is deliberate and `dotnet test` is not
8897
this repository's runner (Microsoft.Testing.Platform is; run the built test executable, or see
8998
[`docs/INDEX.md`](docs/INDEX.md)).
@@ -115,7 +124,47 @@ Use it when you need:
115124

116125
---
117126

118-
## What's new in v2.1.0 (current release)
127+
## What's new in v2.1.2 (current release)
128+
129+
**A data-correctness patch with no API changes.** `ExecuteSQL(sql, parameters)` does not bind values at the
130+
engine level: it renders each one back **into the statement text** and hands that text to the ordinary
131+
parser, so the formatter and the statement scanners have to agree about escaping — and they did not. A
132+
bound `TEXT` value holding an apostrophe, a doubled quote or a backslash was re-scanned as SQL: the value
133+
could land in the **neighbouring column**, and a value ending in a backslash could not be written at all
134+
(`Mismatched parentheses in VALUES clause`). Any consumer storing prose, source code or Windows paths
135+
through a parameter was exposed, and both faces of the failure were silent.
136+
137+
The rules now live once, in [`SqlLiteralScanner`](src/SharpCoreDB/Services/SqlLiteralScanner.cs), and every
138+
literal reader on the statement path uses it: the `VALUES` and `SET` splitters, the matching-parenthesis
139+
scan, all four parameter extractors, the batch `INSERT` parsers, `FastSqlLexer.ReadString`, the expression
140+
tokenizer, the `WHERE` tokenizer (`EvaluateWhere` split on spaces and truncated *any* quoted value at its
141+
first space) and the server's `$n` → `?` translation. A doubled quote is one quote and does not close the
142+
literal; a backslash is data. `ParseValue` removes exactly one quote pair and collapses the doubling (a
143+
parameter holding `'` used to store an empty string), a quoted `''` stores an empty string as SQLite does
144+
instead of `NULL`, `UPDATE … SET col = @v` splits at the first `=` instead of every `=`, and a tuple that
145+
cannot fit its statement throws before any row is written — naming the table, the tuple index, both counts
146+
and the column list — instead of shifting values right.
147+
148+
```csharp
149+
// Before 2.1.2 the body landed in `Source` (or the INSERT threw "Mismatched parentheses").
150+
// After: both round-trip byte-for-byte, Windows paths and prose included.
151+
db.ExecuteSQL("INSERT INTO Passages (Id, Source, Body) VALUES (@id, @src, @body)",
152+
new Dictionary<string, object?>
153+
{
154+
["@id"] = 7,
155+
["@src"] = @"C:\src\app\",
156+
["@body"] = @"It's O(n log n) — see docs\draft.pdf, 'quoted' too"
157+
});
158+
```
159+
160+
Validated on this tree: **core 2,107 tests / 0 failed**, VectorSearch **255 / 0**, EF Core **116 / 0**,
161+
linq2DB **24 / 0**, and `SharpCoreDB.CI.slnf` builds clean. A two-file runnable reproduction with the
162+
before/after console output ships in [`docs/sharpcoredb-known-issues.md`](docs/sharpcoredb-known-issues.md)
163+
§7, and the consumer that reported it — SharpAgent's code-evidence index, which writes source text and
164+
Windows paths as `TEXT` — runs its **144 indexing tests green** against this build with **no code change**.
165+
Full list: [`docs/CHANGELOG.md`](docs/CHANGELOG.md) › `[2.1.2]`.
166+
167+
## What's new in v2.1.0 (previous release)
119168

120169
The v1.x benchmark gap (point reads/updates/deletes **16–52x behind SQLite** because every update
121170
appended a new row) is closed as an engine behaviour. The numbers below are re-measured on
@@ -235,7 +284,7 @@ Headline changes, grouped by area. Everything below is in the shipped `2.1.0` pa
235284
### 1) Embedded mode
236285

237286
```bash
238-
dotnet add package SharpCoreDB --version 2.1.0
287+
dotnet add package SharpCoreDB --version 2.1.2
239288
```
240289

241290
```csharp
@@ -272,8 +321,8 @@ gRPC endpoint: `https://localhost:5001`
272321
Install client/server packages:
273322

274323
```bash
275-
dotnet add package SharpCoreDB.Server.Core --version 2.1.0
276-
dotnet add package SharpCoreDB.Client --version 2.1.0
324+
dotnet add package SharpCoreDB.Server.Core --version 2.1.2
325+
dotnet add package SharpCoreDB.Client --version 2.1.2
277326
```
278327

279328
> The server host is a standalone executable (`src/SharpCoreDB.Server`), distributed as the
@@ -286,6 +335,10 @@ dotnet add package SharpCoreDB.Client --version 2.1.0
286335

287336
## Previous releases
288337

338+
- **v2.1.1** — patch, no API changes: a bound `float[]` reaches a `VECTOR(n)` column as a vector,
339+
`ORDER BY vec_distance_*() LIMIT k` reaches the vector index instead of scanning, `information_schema`
340+
is answered by the engine itself (so REST and the embedded API see the catalog the PG handshake does),
341+
and the 18 % coverage floor is measured and enforced in CI.
289342
- **v2.0.0.3** — the net10.0 / C# 14 stable line, parked on `release/v2.0` and built by `ci-net10.yml`
290343
(the complete package list is above). Engine foundation it carries: fixed-width record layout by default
291344
for new PK tables, in-place UPDATE/DELETE with commit-time tombstones, and the reopen data-integrity
@@ -300,50 +353,50 @@ dotnet add package SharpCoreDB.Client --version 2.1.0
300353

301354
## Available NuGet packages
302355

303-
### v2.1.0 — current release (`master`, net11.0 / C# 15 preview)
356+
### v2.1.2 — current release (`master`, net11.0 / C# 15 preview)
304357

305358
```bash
306359
# Core
307-
dotnet add package SharpCoreDB --version 2.1.0
360+
dotnet add package SharpCoreDB --version 2.1.2
308361

309362
# Server/client
310-
dotnet add package SharpCoreDB.Server.Core --version 2.1.0
311-
dotnet add package SharpCoreDB.Server.Protocol --version 2.1.0
312-
dotnet add package SharpCoreDB.Client --version 2.1.0
313-
dotnet add package SharpCoreDB.Client.Protocol --version 2.1.0
314-
dotnet add package SharpCoreDB.Data.Provider --version 2.1.0
363+
dotnet add package SharpCoreDB.Server.Core --version 2.1.2
364+
dotnet add package SharpCoreDB.Server.Protocol --version 2.1.2
365+
dotnet add package SharpCoreDB.Client --version 2.1.2
366+
dotnet add package SharpCoreDB.Client.Protocol --version 2.1.2
367+
dotnet add package SharpCoreDB.Data.Provider --version 2.1.2
315368

316369
# Engines and extensions
317-
dotnet add package SharpCoreDB.Analytics --version 2.1.0
318-
dotnet add package SharpCoreDB.VectorSearch --version 2.1.0
319-
dotnet add package SharpCoreDB.Search --version 2.1.0
320-
dotnet add package SharpCoreDB.HybridSearch --version 2.1.0
321-
dotnet add package SharpCoreDB.Graph --version 2.1.0
322-
dotnet add package SharpCoreDB.Graph.Advanced --version 2.1.0
323-
dotnet add package SharpCoreDB.Distributed --version 2.1.0
324-
dotnet add package SharpCoreDB.Provider.Sync --version 2.1.0
325-
dotnet add package SharpCoreDB.Provider.YesSql --version 2.1.0
326-
dotnet add package SharpCoreDB.EntityFrameworkCore --version 2.1.0
327-
dotnet add package SharpCoreDB.Extensions --version 2.1.0
328-
dotnet add package SharpCoreDB.Identity --version 2.1.0
329-
dotnet add package SharpCoreDB.Serilog.Sinks --version 2.1.0
370+
dotnet add package SharpCoreDB.Analytics --version 2.1.2
371+
dotnet add package SharpCoreDB.VectorSearch --version 2.1.2
372+
dotnet add package SharpCoreDB.Search --version 2.1.2
373+
dotnet add package SharpCoreDB.HybridSearch --version 2.1.2
374+
dotnet add package SharpCoreDB.Graph --version 2.1.2
375+
dotnet add package SharpCoreDB.Graph.Advanced --version 2.1.2
376+
dotnet add package SharpCoreDB.Distributed --version 2.1.2
377+
dotnet add package SharpCoreDB.Provider.Sync --version 2.1.2
378+
dotnet add package SharpCoreDB.Provider.YesSql --version 2.1.2
379+
dotnet add package SharpCoreDB.EntityFrameworkCore --version 2.1.2
380+
dotnet add package SharpCoreDB.Extensions --version 2.1.2
381+
dotnet add package SharpCoreDB.Identity --version 2.1.2
382+
dotnet add package SharpCoreDB.Serilog.Sinks --version 2.1.2
330383

331384
# Optional architecture packages
332-
dotnet add package SharpCoreDB.EventSourcing --version 2.1.0
333-
dotnet add package SharpCoreDB.Projections --version 2.1.0
334-
dotnet add package SharpCoreDB.CQRS --version 2.1.0
385+
dotnet add package SharpCoreDB.EventSourcing --version 2.1.2
386+
dotnet add package SharpCoreDB.Projections --version 2.1.2
387+
dotnet add package SharpCoreDB.CQRS --version 2.1.2
335388

336389
# Optional functional adapters
337-
dotnet add package SharpCoreDB.Functional --version 2.1.0
338-
dotnet add package SharpCoreDB.Functional.Dapper --version 2.1.0
339-
dotnet add package SharpCoreDB.Functional.EntityFrameworkCore --version 2.1.0
340-
dotnet add package SharpCoreDB.Functional.Linq2DB --version 2.1.0
390+
dotnet add package SharpCoreDB.Functional --version 2.1.2
391+
dotnet add package SharpCoreDB.Functional.Dapper --version 2.1.2
392+
dotnet add package SharpCoreDB.Functional.EntityFrameworkCore --version 2.1.2
393+
dotnet add package SharpCoreDB.Functional.Linq2DB --version 2.1.2
341394

342395
# Hosting and tooling
343-
dotnet add package SharpCoreDB.Aspire.Hosting --version 2.1.0
396+
dotnet add package SharpCoreDB.Aspire.Hosting --version 2.1.2
344397
```
345398

346-
> The 27 commands above are the complete packable set of this line. **`2.1.0` is a stable release**, so
399+
> The 27 commands above are the complete packable set of this line. **`2.1.2` is a stable release**, so
347400
> NuGet selects it by default for a `net11.0` project; a `net10.0` project resolves to the `2.0.0.3` line
348401
> instead. Pin the version explicitly (as above) when you want to be certain, and tick *Include
349402
> prerelease* only if you are deliberately testing an RC build.

‎docs/CHANGELOG.md‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
> **Where this bucket ends.** `2.1.1` was cut from this line's `HEAD` on 2026-10-25, so every entry this
11-
> bucket had collected since the `v2.1.0` tag — the list below — **shipped in `[2.1.1]`**. New entries
12-
> start here.
10+
> **Where this bucket ends.** `2.1.2` was cut from this line's `HEAD` on 2026-10-26, so the entry this
11+
> bucket had collected since the `v2.1.1` tag — the statement-path literal scanner below — **shipped in
12+
> `[2.1.2]`**. New entries start here.
13+
14+
## [2.1.2] - 2026-10-26
15+
16+
> **What this release is.** A patch release with no API changes, cut from `v2.1.1..HEAD`, and a data-correctness
17+
> one rather than a performance one. `ExecuteSQL(sql, parameters)` never binds: it renders every value back
18+
> into the statement text and hands that text to the ordinary parser, so the formatter and the scanners have
19+
> to agree about escaping — and they did not. A bound `TEXT` value holding an apostrophe, a doubled quote or a
20+
> backslash was re-scanned as SQL, and the damage was silent: the value landed in the neighbouring column, or
21+
> the statement was rejected with "Mismatched parentheses in VALUES clause". Any consumer that stores prose,
22+
> source code or Windows paths through a parameter was exposed; SharpAgent's code-evidence index hit both
23+
> faces of it. One scanner now owns the rules (`Services/SqlLiteralScanner.cs`), every literal reader on the
24+
> statement path uses it, and a tuple that still cannot fit its statement fails loudly instead of shifting
25+
> values. Verified on this tree: core **2107 / 0 failed**, VectorSearch **255 / 0**, EF Core **116 / 0**,
26+
> linq2DB **24 / 0**, `SharpCoreDB.CI.slnf` builds clean, and the reproduction published in
27+
> `docs/sharpcoredb-known-issues.md` §7 prints the fixed output when extracted from the document.
28+
29+
### Fixed
30+
31+
- **A bound string parameter was re-parsed as SQL text, so an apostrophe or a backslash in the value corrupted the row — it could land in the next column, or the statement failed with "Mismatched parentheses in VALUES clause".** `ExecuteSQL(sql, parameters)` renders every value back into the statement text (`ParameterBinder.FormatParameter` emits `'it''s'` for `it's`, SQL's doubled-quote convention) and hands the *rendered* statement to the ordinary parser, so the formatter and the scanner have to agree about escaping — and they did not: the VALUES scanner dropped the quote of a doubled pair (standard SQL doubling was not an escape) and let a backslash suppress a quote toggle, which also meant a value ending in a backslash could not be written at all. SharpAgent's code-evidence index hit it as rows rejected with "Mismatched parentheses in VALUES clause" and, worse, as a value silently landing in the neighbouring column (`Invalid VECTOR literal '… iterations)'` on `embedding`). The rules are now SQL's and SQLite's, defined once in `Services/SqlLiteralScanner.cs` and used by every literal reader on the statement path — the VALUES and `SET` splitters, the matching-parenthesis scan, all four parameter extractors, the batch INSERT parsers, `FastSqlLexer.ReadString`, the expression tokenizer, the `WHERE` tokenizer (`Table.Scanning.EvaluateWhere`, which split on spaces and therefore truncated *any* quoted value at its first space) and the wire protocol's `$n` → `?` translation (which needed `InternalsVisibleTo("SharpCoreDB.Server.Core")`): a doubled quote is one quote and does not close the literal, and a backslash is data. `ParseValue` now removes exactly **one** pair of quotes and collapses the doubling — the old `while`-strip removed every edge quote, so a parameter holding `'` stored an empty string, and `'it''s'` reached storage as `it''s`. `UPDATE … SET col = @v` splits at the first `=` instead of every `=` (a value containing `=` silently dropped that assignment), a quoted empty literal stores an empty string as SQLite does (a trailing `''` used to become `NULL`), and a tuple that still produces more values than the statement can consume throws **before any row is written**, naming the table, the tuple index, both counts and the column list, instead of shifting values into the next column. Verified red→green: `ParameterLiteralRoundTripTests` (74 cases — apostrophes, doubled quotes, backslash before a quote, trailing backslash, commas, brackets, braces, newlines, non-ASCII, `NULL`, the empty string, `'`, `''`, `\`, each through an interior and a trailing INSERT value, multi-row INSERT, positional `?`, `UPDATE … SET`, and a `WHERE val = @val` lookup) failed 32 of 57 assertions before the fix and passes 74/74 after; `InsertValuesParsingTests` was corrected to SQL semantics (`'it''s'` → `it's`, a trailing backslash is data, a trailing `''` is an empty string) and the suites are **core 2107 / 0 failed**, VectorSearch 255 / 0, EF Core 116 / 0, linq2DB 24 / 0 (`SharpCoreDB.CI.slnf` builds clean). Two latent test bugs surfaced on the way: `EncryptionCoverageTests` and `WritePathProfilerTests` interpolated `{i * 0.5}` straight into SQL, which renders `0,5` on a comma-decimal machine and is a four-value tuple — the old parser silently dropped the overflow value, the new arity check rejects it, so both now format invariantly. `docs/sharpcoredb-known-issues.md` §7 now carries a self-contained, runnable reproduction (two files, `dotnet run`) with the before/after console output, and the same cases are pinned by `ParameterLiteralRoundTripTests`.
1332

1433
## [2.1.1] - 2026-10-25
1534

0 commit comments

Comments
 (0)