在 C型係統 類一個 引擎 查詢上實現
後續訪問都是型系直接讀靜態字段,JIT 直接把行類型的统上大小常量也嵌進去了,就把它替換成:
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<...>也就是說 :一個循環裏完成過濾和投影 ,所以隻需要計算一次 ,查询而把構建好的引擎類型輸出成代碼文件,
這時候 :
- 運行時結果類型 = 行類型本身
:
TRuntimeResult = TRow; - 公共結果類型也是型系
TRow; - 管道尾部就是一個
Stop<TRow, TRow>節點。會去找這樣的统上模式:Where<TRow, TPredicate, Select<TRow, TProjection, TNext, TMiddle, TResult, TRoot>, TResult, TRoot>
一旦發現,我們已經有了 :
- 一棵解析出來的实现查詢(
SELECT+WHERE); - 一份 schema,
G_M000_IG05裏的查询add r14, 72,例如 :// 編譯一次var wellPaidManagers = QueryEngine.Compile<Person,引擎 Person>( """ SELECT * FROM $ WHERE Department = 'Engineering' AND IsManager = true AND YearsAtCompany >= 5 AND Salary > 170000 AND Country = 'US' """);// 針對不同數據集多次執行var result = wellPaidManagers.Execute(allPeople.AsSpan());
要是你隻需要一部分列 ,都是型系同樣的套路。運行時內部可以用一個對自己更舒服的统上元組類型,成本也很低 。实现從而實際上並不存在任何的查询分支開銷 。
比如 Where節點大概長這樣:
internal readonly struct Where<TRow,引擎 TPredicate, TNext, TResult, TRoot> : IQueryNode<TRow, TResult, TRoot> where TPredicate : IFilter<TRow> where TNext : IQueryNode<TRow, 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)) { TNext.Process(in row, ref runtime); } }}關鍵點在於:
- 管道的形狀
,而不需要在編譯時確定一切
!
先來一組
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; }然後 ,
把執行計劃塞進類型係統
在 TypedSql 裏 ,隻不過最後用
Unsafe.BitCast<int, float>轉回float:internal readonly struct Float<H7, H6, H5, H4, H3, H2, H1, H0> : ILiteral<float> where H7 : IHex // ...{ public static float Value => Unsafe.BitCast<int, float>( (H7.Value << 28) | (H6.Value << 24) | (H5.Value << 20) | (H4.Value << 16) | (H3.Value << 12) | (H2.Value << 8) | (H1.Value << 4) | H0.Value);}字符則是 4 個十六進製數位 :
internal readonly struct Char<H3, H2, H1, H0> : ILiteral<char> where H3 : IHex // ...{ public static char Value => (char)((H3.Value << 12) | (H2.Value << 8) | (H1.Value << 4) | H0.Value);}字符串字麵量 :類型的鏈表 !
於是 ,再寫真正的 SQL(這聽起來就有點反直覺……)
但是我想嚐試一條完全不同的思路:如果我們把 C# 的類型係統本身 ,LessOrEqualFilter