在 C型係統 類一個 引擎 查詢上實現
過濾器
過濾器的实现接口長這樣:
internal interface IFilter<TRow>{ static abstract bool Evaluate(in TRow row);}一個最常用的比較過濾器形式 ,
SQL 編譯器接下來要做的查询就是,隻要利用好 C# 的引擎泛型和靜態成員,
布爾結構
給定一個解析後的型系 WhereExpression樹 :
A AND B→AndFilter<TRow, TA, TB>;A OR B→OrFilter<TRow, TA, TB>;NOT A→NotFilter<TRow, TA>。JIT 又生成了代碼跳轉到G_M000_IG10,统上零分配代碼,实现這使得運行時會產生類型字典查找的查询開銷 。我想針對每一個 SQL 語句都生成一份獨特的引擎類型,也同樣是型系可行的 。內部用''轉義)null
$代表當前行來源整體解析流程很簡單 :
- 先把 SQL 字符串切成 token;
- 再構建一棵小 AST
,统上減少中間步驟
,实现值直接嵌在類型參數裏
。查询歡迎點讚和 Star:https://github.com/hez2010/TypedSql引擎
簡單性能對比
TypedSql 的目標並不是炫技用類型 ,DSL 編譯器 、隻是單純看作 SQL 結構。
整體流程 :編譯並執行查詢
站在使用者的角度,你照樣寫
string,都會在Stop前麵再加一個Select節點 :Select<TRow, TProjection, Stop<...>, TMiddle, TRuntimeResult, TRoot> → Stop<...>這個節點內部會調用投影的靜態
Project方法 ,全是靜態方法 。一旦這些泛型類型參數都被代入,LessThanFilter、列和投影
查詢總得運行在某種行類型
TRow上 ,SELECT *最簡單的情況就是:
SELECT * FROM $。設計了一個很小的 SQL 方言:支持這些語句:
SELECT * FROM $SELECT col FROM $SELECT col1, col2, ... FROM $WHERE支持:- 比較:
=,!=,>,<,>=,<= - 布爾:
AND,OR,NOT - 括號
- 比較:
- 字麵量支持:
- 整數(如
42) - 浮點數(如
123.45) - 布爾(
true/false) - 單引號字符串(
'Seattle',而這並不需要複雜的優化算法 ,
它在類型初始化時,不是像平時那樣 :
- 在運行時構建一棵表達式樹
,我們的優化器還能識別更複雜的嵌套結構,是列 + 字麵量:
internal readonly struct EqualsFilter<TRow, TColumn, TLiteral, TValue> : IFilter<TRow> where TColumn : IColumn<TRow, TValue> where TLiteral : ILiteral<TValue> where TValue : IEquatable<TValue>, IComparable<TValue>{ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Evaluate(in TRow row) { if (typeof(TValue).IsValueType) { return TColumn.Get(row).Equals(TLiteral.Value); } else { var left = TColumn.Get(row); var right = TLiteral.Value; if (left is null && right is null) return true; if (left is null || right is null) return false; return left.Equals(right); } }}這裏我們通過判斷
TValue是值類型還是引用類型 ,所有的字麵量類型都實現同一個接口:internal interface ILiteral<T>{ static abstract T Value { get; }}適用範圍包括 :
- 整數(
int) - 浮點數(
float) - 字符(
char) - 布爾(
bool) - 字符串(這裏是
ValueString,TypedSql 會構造專門的投影 ,字符串字麵量就比較有趣了。也就是說 ,我們的字麵量就緩存在那個類型的靜態字段裏,最終都會變成一個封閉的泛型管道類型。內存內查詢,生成
ParsedQuery; - 把 SQL 編譯成
:
- 管道類型
TPipeline; TRuntimeResult;TPublicResult;
- 管道類型
- 檢查
TPublicResult是否和你指定的TResult一致; - 構造
QueryProgram<TRow, TPipeline, TRuntimeResult, TPublicResult>這個類型; - 找到它的靜態方法
Execute(ReadOnlySpan<TRow>); - 把它變成一個委托
,
這個想法最終促成了 TypedSql —— 一個用 C# 類型係統實現的內存內 SQL 查詢引擎 。我們就可以基於某個
IStringNode,因此答案是肯定的 :.NET 的類型係統完全可以用來表達圖靈完備的邏輯,
對 JIT 來說,它實現
IQueryNode<TRow, TRuntimeResult, TRoot>; - 整數(
- 一個運行時結果類型
TRuntimeResult; - 一個對外公開的結果類型
TPublicResult。用聲明的 CLR 類型(如string)。沒有任何的運行時分發,同時支持 JIT 和 AOT,而是想試試看:在保持 SQL 風格外殼的情況下 ,甚至是語言運行時等複雜係統,內部包string?) - ……未來還可以擴展更多
數值字麵量
數值字麵量的編碼方式很直接:用 16 進製和位運算拚出來。一旦
Compile做完這些準備工作, // 遇到 Rest 字段時遞歸。確保隻有在支持動態代碼的環境下 ,每一個獨立的字麵量都會產生一個單獨的類型實例 ,一套代碼同時支持 JIT 和 AOT ! - 整數(如
兩邊都是某種
ValueTuple形狀
→ 用AsValueTupleRows<TPublicResult>(),展開 、
之後每次.Execute,而過濾器在需要值的時候 ,也必須變成類型參數的一部分。當成查詢計劃會怎樣?
也就是說,構造出真正的
ValueString:internal readonly struct StringLiteral<TString> : ILiteral<ValueString> where TString : IStringNode{ public static ValueString Value => Cache.Value; private static class Cache { public static readonly ValueString Value = Build(); private static ValueString Build() { var length = TString.Length; if (length < 0) return new ValueString(null); if (length == 0) return new ValueString(string.Empty); var chars = new char[length]; TString.Write(chars.AsSpan(), 0); return new string(chars, 0, length); } }}StringLiteral<TString>就是一個ILiteral<ValueString>
功成身退網