Skip to content

Commit e220461

Browse files
author
MPCoreDeveloper
committed
feat: COLLATE Phase 3 - Collation-Aware Query Execution
Added collation support to WHERE/JOIN/IN comparisons with zero-allocation Span<T> helpers. Created 11 unit tests. Build successful.
1 parent fdc86ae commit e220461

Some content is hidden

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

44 files changed

+4036
-157
lines changed

.github/SIMD_STANDARDS.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# SIMD API Standards — SharpCoreDB
2+
3+
> **Mandatory for all SIMD code in SharpCoreDB.** Non-compliant code will be rejected in review.
4+
5+
## Required API: `System.Runtime.Intrinsics`
6+
7+
All SIMD code **MUST** use the explicit intrinsics from `System.Runtime.Intrinsics`:
8+
9+
```csharp
10+
// ✅ REQUIRED — explicit multi-tier intrinsics
11+
using System.Runtime.Intrinsics;
12+
using System.Runtime.Intrinsics.X86;
13+
14+
Vector512<float> v512 = Vector512.LoadUnsafe(ref data);
15+
Vector256<float> v256 = Vector256.LoadUnsafe(ref data);
16+
Vector128<float> v128 = Vector128.LoadUnsafe(ref data);
17+
```
18+
19+
## Banned API: `System.Numerics.Vector<T>`
20+
21+
**DO NOT** use the old portable `Vector<T>` from `System.Numerics`:
22+
23+
```csharp
24+
// ❌ BANNED — old portable SIMD (no explicit ISA control)
25+
using System.Numerics;
26+
27+
Vector<float>.Count; //
28+
Vector.IsHardwareAccelerated; //
29+
MemoryMarshal.Cast<float, Vector<float>>(...); //
30+
Vector.Sum(...); //
31+
```
32+
33+
### Why?
34+
35+
As recommended by Tanner Gooding (.NET Runtime team), `System.Runtime.Intrinsics` is the
36+
modern, preferred API for .NET 8+ / .NET 10:
37+
38+
| Feature | `System.Numerics.Vector<T>` (OLD) | `System.Runtime.Intrinsics` (NEW) |
39+
|---|---|---|
40+
| ISA control | JIT decides width | Explicit per-tier |
41+
| AVX-512 | No explicit support | Full `Avx512F` access |
42+
| FMA | Not accessible | `Fma.MultiplyAdd()` |
43+
| NativeAOT | Width may vary | Deterministic codegen |
44+
| Instruction selection | Opaque | You choose the instruction |
45+
| Multi-tier dispatch | Not possible | AVX-512 → AVX2 → SSE → Scalar |
46+
47+
## Required Multi-Tier Dispatch Pattern
48+
49+
Every SIMD hot path must implement a tiered fallback chain:
50+
51+
```csharp
52+
if (Avx512F.IsSupported && len >= AVX512_THRESHOLD)
53+
{
54+
// Vector512<T> path
55+
}
56+
else if (Avx2.IsSupported && len >= 8) // or Sse.IsSupported for float
57+
{
58+
// Vector256<T> path
59+
}
60+
else if (Sse.IsSupported && len >= 4) // or Sse2 for int/double
61+
{
62+
// Vector128<T> path
63+
}
64+
65+
// Scalar tail — ALWAYS required
66+
for (; i < len; i++) { /* scalar */ }
67+
```
68+
69+
### ISA Check Mapping
70+
71+
| Data Type | 512-bit | 256-bit | 128-bit |
72+
|---|---|---|---|
73+
| `float` | `Avx512F.IsSupported` | `Avx2.IsSupported` | `Sse.IsSupported` |
74+
| `double` | `Avx512F.IsSupported` | `Avx2.IsSupported` | `Sse2.IsSupported` |
75+
| `int` | `Avx512F.IsSupported` | `Avx2.IsSupported` | `Sse2.IsSupported` |
76+
| `long` | `Avx512F.IsSupported` | `Avx2.IsSupported` | `Sse2.IsSupported` |
77+
| `byte` (XOR/popcount) | `Avx512BW.IsSupported` | `Avx2.IsSupported` | `Sse2.IsSupported` |
78+
79+
## Required Patterns
80+
81+
### Loading Data (use `LoadUnsafe`, not pointer-based loads)
82+
83+
```csharp
84+
// ✅ DO — safe ref-based loading (no 'fixed', no unsafe)
85+
ref float refData = ref MemoryMarshal.GetReference(span);
86+
var vec = Vector256.LoadUnsafe(ref Unsafe.Add(ref refData, i));
87+
88+
// ✅ ALSO OK — pointer-based when already in unsafe context
89+
var vec = Avx.LoadVector256(ptr + i);
90+
91+
// ❌ DON'T — old MemoryMarshal.Cast to Vector<T>
92+
var vecs = MemoryMarshal.Cast<float, Vector<float>>(span);
93+
```
94+
95+
### FMA (Fused Multiply-Add)
96+
97+
Always use FMA when available — better throughput AND precision:
98+
99+
```csharp
100+
// ✅ DO — FMA with fallback
101+
if (Fma.IsSupported)
102+
vSum = Fma.MultiplyAdd(va, vb, vSum); // a*b + c in one instruction
103+
else
104+
vSum += va * vb;
105+
106+
// ✅ DO — AVX-512 always has FMA
107+
vSum = Avx512F.FusedMultiplyAdd(va, vb, vSum);
108+
```
109+
110+
### Horizontal Reduction
111+
112+
```csharp
113+
// ✅ DO — cross-platform Vector*.Sum()
114+
float result = Vector256.Sum(vAccumulator);
115+
116+
// ❌ DON'T — old System.Numerics
117+
float result = Vector.Sum(vAccumulator);
118+
```
119+
120+
### Storing Results
121+
122+
```csharp
123+
// ✅ DO
124+
result.StoreUnsafe(ref Unsafe.Add(ref refDst, i));
125+
126+
// ❌ DON'T — old MemoryMarshal.Cast to write
127+
var spanDst = MemoryMarshal.Cast<float, Vector<float>>(result.AsSpan());
128+
spanDst[j] = value;
129+
```
130+
131+
## AVX-512 Thresholds
132+
133+
AVX-512 has a frequency throttling cost on some CPUs. Use minimum element thresholds:
134+
135+
| Use Case | Minimum Elements |
136+
|---|---|
137+
| WHERE filtering (int/double) | 1024 |
138+
| Distance metrics (float) | 64 |
139+
| Batch XOR / Hamming | 32 bytes |
140+
141+
## Reference Implementations
142+
143+
- **WHERE filtering**: `src/SharpCoreDB/Optimizations/SimdWhereFilter.cs`
144+
- **Distance metrics**: `src/SharpCoreDB.VectorSearch/Distance/DistanceMetrics.cs`
145+
- **Hamming distance**: `src/SharpCoreDB.VectorSearch/Quantization/BinaryQuantizer.cs`
146+
147+
## Code Review Checklist
148+
149+
- [ ] No `System.Numerics.Vector<T>` usage
150+
- [ ] No `Vector.IsHardwareAccelerated` checks
151+
- [ ] Uses `Vector128<T>` / `Vector256<T>` / `Vector512<T>` from `System.Runtime.Intrinsics`
152+
- [ ] Multi-tier dispatch: AVX-512 → AVX2 → SSE → Scalar
153+
- [ ] Scalar tail for remainder elements
154+
- [ ] FMA used where available (`Fma.MultiplyAdd` / `Avx512F.FusedMultiplyAdd`)
155+
- [ ] `[MethodImpl(MethodImplOptions.AggressiveOptimization)]` on hot paths
156+
- [ ] AVX-512 guarded by minimum element threshold
157+
158+
---
159+
160+
**Enforcement:** All new and modified SIMD code must comply. Existing violations should be migrated on contact.
161+
**Last Updated:** 2025-07-08

SharpCoreDB.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefFieldDemo", "tests\Manua
7171
EndProject
7272
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCoreDB.VectorSearch", "src\SharpCoreDB.VectorSearch\SharpCoreDB.VectorSearch.csproj", "{5E39577A-E286-45E9-9801-E8DC8F81ED7D}"
7373
EndProject
74+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCoreDB.VectorSearch.Tests", "tests\SharpCoreDB.VectorSearch.Tests\SharpCoreDB.VectorSearch.Tests.csproj", "{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}"
75+
EndProject
7476
Global
7577
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7678
Debug|Any CPU = Debug|Any CPU
@@ -285,6 +287,18 @@ Global
285287
{5E39577A-E286-45E9-9801-E8DC8F81ED7D}.Release|x64.Build.0 = Release|Any CPU
286288
{5E39577A-E286-45E9-9801-E8DC8F81ED7D}.Release|x86.ActiveCfg = Release|Any CPU
287289
{5E39577A-E286-45E9-9801-E8DC8F81ED7D}.Release|x86.Build.0 = Release|Any CPU
290+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
291+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|Any CPU.Build.0 = Debug|Any CPU
292+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|x64.ActiveCfg = Debug|Any CPU
293+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|x64.Build.0 = Debug|Any CPU
294+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|x86.ActiveCfg = Debug|Any CPU
295+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Debug|x86.Build.0 = Debug|Any CPU
296+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|Any CPU.ActiveCfg = Release|Any CPU
297+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|Any CPU.Build.0 = Release|Any CPU
298+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|x64.ActiveCfg = Release|Any CPU
299+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|x64.Build.0 = Release|Any CPU
300+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|x86.ActiveCfg = Release|Any CPU
301+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA}.Release|x86.Build.0 = Release|Any CPU
288302
EndGlobalSection
289303
GlobalSection(SolutionProperties) = preSolution
290304
HideSolutionNode = FALSE
@@ -313,6 +327,7 @@ Global
313327
{53FBF8A2-0A92-44AD-9CD0-4FBDF7F62C9B} = {2F8A8533-DAA8-4CF9-A6C0-2F663AF7FD2E}
314328
{800EBE19-FE8B-EBD4-D5F7-AFB27C1755A0} = {A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D}
315329
{5E39577A-E286-45E9-9801-E8DC8F81ED7D} = {F8B5E3A4-1C2D-4E5F-8B9A-1D2E3F4A5B6C}
330+
{A55A128B-6E04-4FC5-A3FF-6F05F111FECA} = {A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D}
316331
EndGlobalSection
317332
GlobalSection(ExtensibilityGlobals) = postSolution
318333
SolutionGuid = {F40825F5-26A1-4E85-9D0A-B0121A7ED5F8}

docs/CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### ✨ Added
1111
- **Vector Search Extension** (`SharpCoreDB.VectorSearch` NuGet package)
1212
- SIMD-accelerated distance metrics: cosine, Euclidean (L2), dot product
13-
- `System.Numerics.Vector<float>` auto-selects widest SIMD: AVX-512 → AVX2 → SSE2/NEON → scalar
13+
- Multi-tier dispatch: AVX-512 → AVX2 → SSE → scalar with FMA when available
1414
- HNSW approximate nearest neighbor index with configurable M, efConstruction, efSearch
1515
- Flat (brute-force) exact search index for small datasets or perfect recall
1616
- Binary format for vector serialization with magic bytes, version header, and zero-copy spans
@@ -20,9 +20,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Seven SQL functions: `vec_distance_cosine`, `vec_distance_l2`, `vec_distance_dot`, `vec_from_float32`, `vec_to_json`, `vec_normalize`, `vec_dimensions`
2121
- DI registration: `services.AddVectorSupport()` with configuration presets (Embedded, Standard, Enterprise)
2222
- Zero overhead when not registered — all vector support is 100% optional
23+
- **Query Planner: Vector Index Acceleration** (Phase 5.4)
24+
- Detects `ORDER BY vec_distance_*(col, query) LIMIT k` patterns automatically
25+
- Routes to HNSW/Flat index instead of full table scan + sort
26+
- `VectorIndexManager` manages live in-memory index instances per table/column
27+
- `VectorQueryOptimizer` implements `IVectorQueryOptimizer` for pluggable optimization
28+
- `CREATE VECTOR INDEX` now builds live in-memory index immediately
29+
- `DROP VECTOR INDEX` cleans up live index from registry
30+
- `EXPLAIN` shows "Vector Index Scan (HNSW)" or "Vector Index Scan (Flat/Exact)"
31+
- Fallback to full scan when no index exists — zero behavioral change for existing queries
2332
- **Core: Extension Provider System**
2433
- `ICustomFunctionProvider` interface for pluggable SQL functions
2534
- `ICustomTypeProvider` interface for pluggable data types
35+
- `IVectorQueryOptimizer` interface for vector query acceleration
2636
- `DataType.Vector` enum value (stored as BLOB internally)
2737
- `VECTOR(N)` column type parsing in CREATE TABLE
2838
- `ColumnDefinition.Dimensions` for VECTOR(N) metadata
@@ -31,6 +41,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3141
- `CREATE VECTOR INDEX idx ON table(col) USING FLAT|HNSW`
3242
- `DROP VECTOR INDEX idx ON table`
3343
- Vector column type validation at index creation time
44+
- **SIMD Standards** (`.github/SIMD_STANDARDS.md`)
45+
- Mandatory `System.Runtime.Intrinsics` API for all SIMD code
46+
- Multi-tier dispatch pattern (AVX-512 → AVX2 → SSE → scalar)
47+
- FMA support for fused multiply-add
48+
- Banned `System.Numerics.Vector<T>` (old portable SIMD)
3449

3550
### 📊 Version Info
3651
- **Package Version**: 1.2.0

docs/COLLATE_ISSUE_BODY.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## Feature: SQL COLLATE Support for Case-Insensitive and Locale-Aware String Comparisons
2+
3+
### Summary
4+
5+
Add SQL-standard `COLLATE` support to SharpCoreDB, enabling case-insensitive and locale-aware string comparisons at the column level, index level, and query level.
6+
7+
### Motivation
8+
9+
Currently, all string comparisons in SharpCoreDB are binary (case-sensitive). Users need the ability to:
10+
- Define case-insensitive columns (e.g., `Name TEXT COLLATE NOCASE`)
11+
- Have indexes automatically respect collation (case-insensitive lookups)
12+
- Override collation at query time
13+
- Eventually support locale-aware sorting (e.g., German ß, Turkish İ)
14+
15+
### Target SQL Syntax
16+
17+
```sql
18+
-- Column-level collation in DDL
19+
CREATE TABLE Users (
20+
Id INTEGER PRIMARY KEY AUTO,
21+
Name TEXT COLLATE NOCASE,
22+
Email TEXT COLLATE NOCASE
23+
);
24+
25+
-- Index automatically inherits column collation
26+
CREATE INDEX idx_users_name ON Users(Name); -- case-insensitive automatically
27+
28+
-- Query-level override (future)
29+
SELECT * FROM Users WHERE Name COLLATE NOCASE = @var;
30+
SELECT * FROM Users WHERE LOWER(Name) = LOWER(@name);
31+
32+
-- Locale-aware indexes (future)
33+
CREATE INDEX idx_name_ci ON users (name COLLATE "en_US" NOCASE);
34+
CREATE INDEX idx_name_cs ON users (name); -- default is case-sensitive
35+
```
36+
37+
### EF Core Integration (Future)
38+
39+
```csharp
40+
modelBuilder.Entity<User>()
41+
.Property(u => u.Name)
42+
.UseCollation("NOCASE");
43+
```
44+
45+
### Implementation Plan
46+
47+
📄 **Full plan:** [`docs/COLLATE_SUPPORT_PLAN.md`](https://github.com/MPCoreDeveloper/SharpCoreDB/blob/master/docs/COLLATE_SUPPORT_PLAN.md)
48+
49+
### Phases
50+
51+
| Phase | Description | Priority | Impact |
52+
|-------|-------------|----------|--------|
53+
| **Phase 1** | Core types (`CollationType` enum), ITable/Table metadata, persistence | P0 | Foundation — 7 files |
54+
| **Phase 2** | DDL parsing (`COLLATE` in `CREATE TABLE` and `ALTER TABLE ADD COLUMN`) | P0 | `SqlParser.DDL.cs`, `EnhancedSqlParser.DDL.cs` |
55+
| **Phase 3** | Collation-aware WHERE filtering, JOIN conditions, ORDER BY | P0 | `SqlParser.Helpers.cs`, `CompiledQueryExecutor.cs` |
56+
| **Phase 4** | Index integration — HashIndex/BTree key normalization | P1 | `HashIndex.cs`, `BTree.cs`, `GenericHashIndex.cs` |
57+
| **Phase 5** | Query-level `COLLATE` override + `LOWER()`/`UPPER()` functions | P2 | Enhanced parser + AST nodes |
58+
| **Phase 6** | Locale-aware collations (ICU-based, culture-specific) | P3 | Future/research |
59+
| **EF Core** | `UseCollation()` fluent API + DDL emission | Separate | `SharpCoreDBMigrationsSqlGenerator.cs` |
60+
61+
### Codebase Impact (from investigation)
62+
63+
**20+ files** across core engine, SQL parsers, indexes, metadata, and EF Core provider.
64+
65+
Key touchpoints identified:
66+
- `EvaluateOperator()` — currently uses `rowValueStr == value` (binary only)
67+
- `CompareKeys()` in BTree — uses `string.CompareOrdinal()` (binary only)
68+
- `HashIndex` — uses `SimdHashEqualityComparer` (binary only)
69+
- `ColumnDefinition` — missing `Collation` property
70+
- `ITable` / `Table` — missing `ColumnCollations` per-column list
71+
- `SaveMetadata()` — missing collation serialization
72+
- `ColumnInfo` — missing collation in metadata discovery
73+
74+
### Backward Compatibility
75+
76+
- ✅ Default behavior unchanged (all existing tables default to `Binary`)
77+
- ✅ Metadata migration: missing `ColumnCollations` → all Binary
78+
- ✅ All new parameters are optional with Binary defaults
79+
- ✅ Existing indexes continue to work
80+
81+
### Labels
82+
83+
`enhancement`, `sql-engine`, `roadmap`

0 commit comments

Comments
 (0)