/// <summary> /// Constructor /// </summary> internal QueryEngine(QueryPlan <T> queryPlan) { this.table = queryPlan.Table; this.queryPlan = queryPlan; this.paralellism = table.DbContext.Configuration.QueryParallelism; this.filterExpressions = queryPlan.Query.FlexFilters; this.sorting = queryPlan.Query.Sorting; this.indexFilters = queryPlan.Query.IndexFilters; this.limit = this.queryPlan.Query.QueryLimit; var bookmark = this.queryPlan.Query.Bookmark; if (this.table.DbContext.Configuration.DatabasePath != null) { pathPrefix = Path.Join( this.table.DbContext.Configuration.DatabasePath, table.Annotation.Name, queryPlan.BestIndex.Name ); } else { pathPrefix = Path.Join( "var", "data", table.Annotation.Name, queryPlan.BestIndex.Name ); } /* * Check if the bookmark contains a fragment for this engine. */ if (bookmark != null) { this.bookmarkFragment = null; foreach (var fragment in bookmark.Fragments) { if (fragment.TableName == this.table.Annotation.Name && fragment.IndexName == this.queryPlan.BestIndex.Name) { this.bookmarkFragment = fragment; break; } } if (this.bookmarkFragment == null) { throw new FatCatException($"Invalid bookmark. Please always use the bookmarks in the same " + "queries they were created for. (2)"); } } }
internal PacketLoaderTask(Packet <T> packet, QueryPlan <T> queryPlan, bool isAsync) { this.Packet = packet; this.queryPlan = queryPlan; this.isAsync = isAsync; if (isAsync) { this.task = WorkerAsync(); } else { ThreadPool.QueueUserWorkItem(this.WorkerCallback); } }
/// <summary> /// Decompress the raw data and desirialize the records. /// </summary> /// <param name="queryPlan">Optional for queries. Filters the recrods if passed.</param> internal void DeserializeDecompress(QueryPlan <T> queryPlan = null) { if (rawCompressedData == null) { return; } List <string> header = null; TsvMapping <T> tsvMapping = null; using var ms = new MemoryStream(rawCompressedData); using var gzip = new GZipStream(ms, CompressionMode.Decompress); using var tsv = new TsvReader(gzip); int lineCount = 0; Nullable <int> tsvColumnIndex = null; bool isFirstLine = true; bool notMatching = false; while (!tsv.EndOfStream) { var line = tsv.ReadLine(); if (isFirstLine) { // Use its own header for compatibility. header = line; isFirstLine = false; tsvMapping = new TsvMapping <T>(table, header); continue; } if (header.Count != line.Count) { throw new Exception($"Header length and column count mismatch in file '{FullPath}' at line {lineCount + 1}."); } /* * Filtering by 'Where' expressions */ if (queryPlan != null) { notMatching = false; foreach (var filter in queryPlan.FreeIndexFilters) { tsvColumnIndex = tsvMapping.FromRecordToTsv[filter.Key]; if (tsvColumnIndex == null || line[(int)tsvColumnIndex] != filter.Value) { notMatching = true; break; } } if (notMatching) { continue; } } var record = new T(); table.LoadFromTSVLine(tsvMapping, record, line); /* * Filtering by flex expressions */ if (queryPlan != null) { notMatching = false; foreach (var exp in queryPlan.Query.FlexFilters) { if (!exp(record)) { notMatching = true; break; } } if (notMatching) { continue; } } string unique = table.GetUnique(record); this.data[unique] = record; this.lines.Add(record); lineCount++; } /* * Sort records inside the packet */ if (queryPlan != null) { if (queryPlan.FreeSorting.Count > 0) { var props = table.Properties.ToArray(); this.lines.Sort((x, y) => { foreach (var directive in queryPlan.FreeSorting) { int dir = directive.Item2 == SortingDirection.Ascending ? 1 : -1; var valueX = props[directive.Item1].GetValue(x); var valueY = props[directive.Item1].GetValue(y); if (valueX == null && valueY == null) { continue; } if (valueX == null) { return(-dir); } if (valueY == null) { return(dir); } return(((IComparable)valueX).CompareTo((IComparable)valueY) * dir); } return(0); }); } } }
/// <summary> /// Returns a user-friendly text that describes the query plan. /// </summary> public string GetQueryPlan() { var plan = new QueryPlan <T>(this); return(plan.ToString()); }
/// <summary> /// Constructor /// </summary> /// <param name="query">An object, specifying the query</param> public Cursor(Query <T> query) { this.table = query.Table; this.queryPlan = new QueryPlan <T>(query); this.queryEngine = new QueryEngine <T>(queryPlan); }