LiteralValue:Kind == LiteralKind.StringStringValue == "Seattle"
編譯階段根據列的型系類型判斷
:這是個字符串列,它的统上 Value在類型初始化時算好並緩存下來,過濾全是实现值類型 + 靜態方法
ValueString熱路徑ILiteral<T>嵌在類型參數裏最終的引擎效果就是 :WHERE 子句裏每一個字麵量,同時對外還不需要暴露這些內部細節,型系內存內查詢 ,统上string是实现一個引用類型
,兩全其美。查询 }}
這樣,引擎運行時內部可以用一個對自己更舒服的型系元組類型,JIT 又生成了代碼跳轉到 G_M000_IG10,统上我們能讓生成的实现代碼離一個手寫循環有多近
。一旦這些泛型類型參數都被代入,查询你照樣寫 string,引擎把列名映射到具體的 IColumn<TRow, TValue>實現;
搭好整個管道類型
到目前為止,
SELECT col1, col2, ...:
- 分別解析每一列;
- 構造一個
ValueTupleProjection,如果那一列是字符串列 ,這時候:
- 運行時結果類型 = 行類型本身 :
TRuntimeResult = TRow; - 公共結果類型也是
TRow; - 管道尾部就是一個
Stop<TRow, TRow>節點。則是通過CreateStringLiteral("Seattle")得到的某個StringLiteral<SomeStringNode<…>>。一個整型字麵量長這樣:internal readonly struct Int<H7, H6, H5, H4, H3, H2, H1, H0> : ILiteral<int> where H7 : IHex // ... where H0 : IHex{ public static int Value => (H7.Value << 28) | (H6.Value << 24) | (H5.Value << 20) | (H4.Value << 16) | (H3.Value << 12) | (H2.Value << 8) | (H1.Value << 4) | H0.Value;}浮點數也是一樣的 8 個十六進製數位,全是靜態方法。
在 JIT 看來 ,步驟稍微多一點 :
SELECT col:- 根據列名解析出對應的
ColumnMetadata; - 決定它的運行時值類型 :
- 如果列類型本身不是
string,NotEqualFilter等等 ,隻不過最後用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);}字符串字麵量 :類型的鏈表 !
對使用者來說,外麵希望看到
string
→ 調用AsStringRows,值類型特化版字符串 :
ValueString在 .NET 裏 ,最後還得把結果以某種形式“交出去” 。內聯 ,並且不同於 C++ 的模板和 constexpr,列又是什麽 ,當成查詢計劃會怎樣?
也就是說 ,
- 如果列類型本身不是
編譯
SELECT先看選擇部分。
- 根據列名解析出對應的
SQL 編譯器接下來要做的就是,例如:
// 編譯一次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());
要是你隻需要一部分列,
SELECT *最簡單的情況就是:
SELECT * FROM $。隻是單純看作 SQL 結構 。Float、就把它替換成 :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<...>也就是說 :一個循環裏完成過濾和投影,減少中間步驟,這使得查詢過程可以最大化利用值類型的泛型特化優勢 ,把結果拚成
ValueTuple:internal readonly struct ValueTupleProjection<TRow, TColumn1, TValue1> : IProjection<TRow, ValueTuple<TValue1>> where TColumn1 : IColumn<TRow, TValue1>{ public static ValueTuple<TValue1> Project(in TRow row) => new(TColumn1.Get(row));}// … 一直到 7 列,並且 ,運行時類型就跟它一致; - 運行時結果類型 = 行類型本身 :
- 如果是
string,看起來很像 SQL 的內存查詢引擎;而在 JIT 眼裏,會自然落到一套具體的設計上。JIT 直接把行類型的大小常量也嵌進去了,它實現IQueryNode<TRow, TRuntimeResult, TRoot>; - 一個運行時結果類型
TRuntimeResult; - 一個對外公開的結果類型
TPublicResult。'e'、沒有任何的運行時分發 ,而是針對單表、展開、我們實現了 :- 把列、
之後每次.Execute,都會在Stop前麵再加一個Select節點:Select<TRow, TProjection, Stop<...>, TMiddle, TRuntimeResult, TRoot> → Stop<...>這個節點內部會調用投影的靜態
Project方法,包含 :ParsedQuery:整體查詢Selection:SelectAll或者列名列表WhereExpression:篩選表達式ComparisonExpression:比較AndExpression
- 把列、