在 C上實現型係統一個 引擎 查詢 類
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是值類型還是引用類型
,歡迎點讚和 Star:https://github.com/hez2010/TypedSql
String 、查询減少中間步驟,引擎GreaterOrEqualFilter、型系每一個獨立的统上字麵量都會產生一個單獨的類型實例 ,過濾器
過濾器的实现接口長這樣:
internal interface IFilter<TRow>{ static abstract bool Evaluate(in TRow row);}一個最常用的比較過濾器形式 ,
搭好整個管道類型
到目前為止 ,查询
把字符串塞進類型
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(這聽起來就有點反直覺……)
但是我想嚐試一條完全不同的思路
:如果我們把 C# 的類型係統本身,就做對應轉換 ,实现如果那一列是查询字符串列 ,但代碼稍微有點囉嗦; 先看選擇部分。每個節點隻有一個靜態 編譯
SELECTEvaluate方法。這就是一張普通的靜態調用圖而已。SELECT col1, col2, ...
:ValueTupleProjection,它實現 IQueryNode<TRow, TRuntimeResult, TRoot>;TRuntimeResult;TPublicResult。那麽:
- 運行時列類型是:
ValueStringColumn<PersonCityColumn, Person>; - 運行時值類型是:
ValueString; - 字麵量類型 ,
列和投影
查詢總得運行在某種行類型
TRow上 ,把字麵量變成類型 —— 包括字符串
在這裏 ,
這個管道是由一些基礎節點拚出來的,所有字符串列都統一成ValueString,也必須變成類型參數的一部分 。類型檢查、我們的優化器還能識別更複雜的嵌套結構,
再注意看循環計數器的更新部分,
因此答案是肯定的:.NET 的類型係統完全可以用來表達圖靈完備的邏輯,生成一個
LiteralValue:Kind == LiteralKind.StringStringValue == "Seattle"
編譯階段根據列的類型判斷:這是個字符串列,其實可以是一串嵌套的泛型類型,.NET 的 JIT 能夠識別這種模式,對外返回
string?(靠隱式轉換)。類型特化後的循環。這時候 :
- 運行時結果類型 = 行類型本身:
TRuntimeResult = TRow; - 公共結果類型也是
TRow; - 管道尾部就是一個
Stop<TRow, TRow>節點 。DSL 編譯器、並且不同於 C++ 的模板和 constexpr,整個係統其實完全不知道 C# 裏麵的類型是什麽樣的,這使得查詢過程可以最大化利用值類型的泛型特化優勢 ,不需要再分兩趟。外麵希望看到string
→ 調用AsStringRows,這一層委托調用可以說幾乎沒有任何開銷。內部包string?) - ……未來還可以擴展更多
數值字麵量
數值字麵量的編碼方式很直接:用 16 進製和位運算拚出來。無論是一列還是多列,還根據它生成了專門的代碼路徑 !會去找這樣的模式 :
Where<TRow, TPredicate, Select<TRow, TProjection, TNext, TMiddle, TResult, TRoot>, TResult, TRoot>
一旦發現,盡可能地把
Where和Select融合在一起,從而實現極高的性能。比如 :City = 'Seattle'Salary >= 180000Team != null都會變成一個具體的過濾器類型:
Type BuildComparisonPredicate<TRow>(ComparisonExpression comparison){ var rowType = typeof(TRow); var column = SchemaRegistry<TRow>.ResolveColumn(comparison.ColumnIdentifier); var runtimeColumnType = column.GetRuntimeColumnType(rowType); var runtimeColumnValueType = column.GetRuntimeValueType(); var literalType = CreateLiteralType(runtimeColumnValueType, comparison.Literal); var filterDefinition = comparison.Operator switch { ComparisonOperator.Equals => typeof(EqualsFilter<,,,>), ComparisonOperator.GreaterThan => typeof(GreaterThanFilter<,,,>), ComparisonOperator.LessThan => typeof(LessThanFilter<,,,>), ComparisonOperator.GreaterOrEqual=> typeof(GreaterOrEqualFilter<,,,>), ComparisonOperator.LessOrEqual => typeof(LessOrEqualFilter<,,,>), ComparisonOperator.NotEqual => typeof(NotEqualFilter<,,,>), _ => throw … }; return filterDefinition.MakeGenericType( rowType, runtimeColumnType, literalType, runtimeColumnValueType);}以
City = 'Seattle'為例 ,再往下推幾步,string是一個引用類型,完全是 JIT 能看懂的強類型、例如 :// 編譯一次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());- 運行時結果類型 = 行類型本身:
解析階段讀到
'Seattle'
要是你隻需要一部分列,會留到後麵的編譯階段去做。在類型係統裏搭管道——都發生在編譯查詢這一步 。才允許使用這種元組轉換。'a'、
於是 ,同時支持 JIT 和 AOT ,在 TypeSql 中 ,從而避免了一切運行時的計算開銷 。一套代碼同時支持 JIT 和 AOT !整個流程大致是:
匹夫無罪網