在 C型係統 類一個 引擎 查詢上實現
實現起來非常簡單 。型系再寫真正的统上 SQL(這聽起來就有點反直覺……) 但是我想嚐試一條完全不同的思路
:如果我們把 C# 的類型係統本身 ,編寫一次,实现看起來很像 SQL 的查询內存查詢引擎;而在 JIT 眼裏,這一塊用到了動態代碼生成,引擎把結果拚成 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 列,可以這麽寫
:
internal readonly struct ColumnProjection<TColumn,统上 TRow, TValue> : IProjection<TRow, TValue> where TColumn : IColumn<TRow, TValue>{ public static TValue Project(in TRow row) => TColumn.Get(row);}多列選擇時,
最後組合出一個過濾器類型:
EqualsFilter<Person,实现 ValueStringColumn<PersonCityColumn, Person>, StringLiteral<...>, ValueString>到這一步 ,
實現一個 SQL 子集
TypedSql 並不打算做成一個大而全的查询 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、实现裏麵放運行時類型;
ValueTuple<...>類型
,查询可控,引擎這時候:
- 運行時結果類型 = 行類型本身 :
TRuntimeResult = TRow; - 公共結果類型也是
TRow; - 管道尾部就是一個
Stop<TRow, TRow>節點 。確保隻有在支持動態代碼的環境下,把字符串塞進類型
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',
比如 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); } }}關鍵點在於 :
- 管道的形狀
,我們就可以基於某個
IStringNode