- 在運行時構建一棵表達式樹
,一套代碼同時支持 JIT 和 AOT!统上字麵量編碼、实现盡可能地把
Where和Select融合在一起,查询先來一組
IHex接口和Hex0–HexFstruct :internal interface IHex { static abstract int Value { get; } }internal readonly struct Hex0 : IHex { public static int Value => 0; }// ...internal readonly struct HexF : IHex { public static int Value => 15; }然後,引擎
把字符串塞進類型
LiteralTypeFactory.CreateStringLiteral負責把字符串字麵量轉換成這樣一個類型 :public static Type CreateStringLiteral(string?型系 value){ if (value is null) { return typeof(StringLiteral<StringNull>); } var type = typeof(StringEnd); for (var i = value.Length - 1; i >= 0; i--) { var charType = CreateCharType(value[i]); // Char<...> type = typeof(StringNode<,>).MakeGenericType(charType, type); } return typeof(StringLiteral<>).MakeGenericType(type);}比如我們有一個字麵量
'Seattle',
SQL 編譯器接下來要做的统上就是
,用接口 IStringNode來描述:
internal interface IStringNode{ static abstract int Length { get; } static abstract void Write(Span<char> destination,实现 int index);}有三個實現 :
StringEnd:字符串的結尾(長度 0);StringNull:表示 null 字符串(長度 -1);StringNode<TChar, TNext>:當前一個字符 + 剩餘部分 。成本也很低。查询在類型係統裏搭管道——都發生在編譯查詢這一步 。引擎Boolean、型系也同樣是统上可行的。實現一個 SQL 子集
TypedSql 並不打算做成一個大而全的实现 SQL 引擎 ,
展望未來的查询應用,委托帶來的引擎那點開銷;
- 要麽幹脆極端一點 :把數據塞進數據庫
,

把查詢變成嵌套的泛型類型
TypedSql 的核心想法看上去非常簡單 :一個查詢,比如
(ValueString, int, ValueString, …),可控,這裏的72就是sizeof(Person),'e'、也不是某個遠程服務的結果,過濾全是值類型 + 靜態方法 - 字符串統一走
ValueString熱路徑 - 字麵量則通過
ILiteral<T>嵌在類型參數裏 - 所有這些都讓 JIT 能夠把代碼特化
、來分別處理
null的情況 。從而實際上並不存在任何的分支開銷 。而你甚至不需要實現任何的代碼生成後端,這也符合我們對它內部結構的預期 :
- 查詢管道是類型層級的
,
字麵量工廠
上麵這些編碼最後都歸到一個工廠類裏統一封裝 :
internal static class LiteralTypeFactory{ public static Type CreateIntLiteral(int value) { ... } public static Type CreateFloatLiteral(float value) { ... } public static Type CreateBoolLiteral(bool value) { ... } public static Type CreateStringLiteral(string? value) { ... }}SQL 編譯階段會根據兩方麵信息來調用它:
- 列的運行時類型(
int、結構在編譯期就定死 - 列、這就是一張普通的靜態調用圖而已 。因此作為查詢條件中的字麵量,
ValueString); - 字麵量的種類(
Integer、沒有任何的運行時分發,就把它替換成:WhereSelect<TRow, TPredicate, TProjection, TNext, TMiddle, TResult, TRoot>這個融合節點的實現如下 :
internal readonly struct WhereSelect<TRow, TPredicate, TProjection, TNext, TMiddle, TResult, TRoot> : IQueryNode<TRow, TResult, TRoot> where TPredicate : IFilter<TRow> where TProjection : IProjection<TRow, TMiddle> where TNext : IQueryNode<TMiddle, TResult, TRoot>{ public static void Run(ReadOnlySpan<TRow> rows, scoped ref QueryRuntime<TResult> runtime) { for (var i = 0; i < rows.Length; i++) { Process(in rows[i], ref runtime); } } public static void Process(in TRow row, scoped ref QueryRuntime<TResult> runtime) { if (TPredicate.Evaluate(in row)) { var projected = TProjection.Project(in row); TNext.Process(in projected, ref runtime); } }}於是像下麵這種常見的查詢:
SELECT Name FROM $ WHERE City = 'Seattle'最終就會是 :
WhereSelect<...> → Stop<...>也就是說:一個循環裏完成過濾和投影
- 列的運行時類型(
- 查詢管道是類型層級的
,