public void CacheObjectWhenCPPChanges() { TestUtils.ResetLINQLibrary(); // Make sure that when the C++ code changes, the cache key for lookup of results will also change. var q = new QueriableDummy <ntup>(); var i = q.Select(e => DoItClass.DoIt(e.run)).Where(x => x > 2).Count(); var query = DummyQueryExectuor.LastQueryModel; // Look for a hash value. var str1 = FormattingQueryVisitor.Format(query); Console.WriteLine(str1); var m = Regex.Match(str1, @"DoIt([^\(]+)"); Assert.IsTrue(m.Success); }
/// <summary> /// The main entry point that we watch - we generate a method info stub when we need it /// from this. /// </summary> /// <param name="queryModel"></param> public override void VisitQueryModel(QueryModel queryModel) { // If the type is something that is friendly to be returned from a // method, then we should cache this guy. _qmContextStack.Push(new QMContext()); // Now, run through the query model. base.VisitQueryModel(queryModel); // And if the QM result type is something we can reasonably cache, then we should do it. // - Do not cache the outter most QM. This guy has the best place to start combining things. // - Do not cache anything that is enumerable. We'll have to deal with that later. // - Do not cache any anonymous types // - Deal with later somethign that is an iterator (used in a later loop). var isEnumerable = typeof(IEnumerable).IsAssignableFrom(queryModel.GetResultType()); var selectSequence = !queryModel.ResultOperators.Any(); if (_qmContextStack.Count > 1 && ((selectSequence && isGoodSelectSequenceType(queryModel.GetResultType())) || !isEnumerable) && !queryModel.GetResultType().IsClass ) { var qmText = FormattingQueryVisitor.Format(queryModel); if (!FoundFunctions.Where(ff => ff.QMText == qmText).Any()) { var sref = _qmContextStack.Peek(); var f = new QMFuncHeader() { QM = queryModel, QMText = qmText, Arguments = sref._arguments.Cast <object>(), IsSequence = selectSequence }; FoundFunctions.Add(f); } } // Go back to working on the previous qm. _qmContextStack.Pop(); }
public void CacheWhenCPPChanges() { // Make sure that when the C++ code changes, the cache key for lookup of results will also change. var q = new QueriableDummy <ntup>(); var mym = new MyModifiableCode(); mym.LOC = new string[] { "int i = 0;", "MultBy2 = 10;" }; var i = q.Select(e => mym.MultBy2(e.run)).Where(x => x > 2).Count(); var query = DummyQueryExectuor.LastQueryModel; // Set the LOC to something. var str1 = FormattingQueryVisitor.Format(query); mym.LOC = new string[] { "int j = 10;", "MultBy2 = 10;" }; var str2 = FormattingQueryVisitor.Format(query); Console.WriteLine(str1); Console.WriteLine(str2); Assert.AreNotEqual(str1, str2); }
/// <summary> /// Return the key object /// </summary> /// <param name="rootfiles"></param> /// <param name="treename"></param> /// <param name="inputObjects"></param> /// <param name="query"></param> /// <returns></returns> public IQueryResultCacheKey GetKey(Uri[] unsortedRootfiles, string treename, object[] inputObjects, string[] unsortedCrumbs, QueryModel query, bool recheckDates = false, Func <Uri, DateTime> dateChecker = null) { /// /// Quick check to make sure everything is good /// TraceHelpers.TraceInfo(23, "GetKey: Initial query calculation"); if (unsortedRootfiles.Any(f => f == null)) { throw new ArgumentException("one of the root files is null"); } if (string.IsNullOrWhiteSpace(treename)) { throw new ArgumentException("tree name must be valid"); } if (inputObjects != null && inputObjects.Any(o => o == null)) { throw new ArgumentException("one of the input objects is null - not allowed"); } /// /// Build the hash, which is a bit of a pain in the butt. /// For the root files we don't care about the order given to us in or the order they /// are processed in. What we care about is what is there! /// var rootfiles = (from r in unsortedRootfiles orderby r.OriginalString ascending select r).ToArray(); TraceHelpers.TraceInfo(24, "GetKey: Creating big string file name and calculating hash"); int fnameLength = rootfiles.Select(f => f.OriginalString).Sum(w => w.Length) + 100; StringBuilder fullSourceName = new StringBuilder(fnameLength); foreach (var f in rootfiles) { fullSourceName.Append(f.OriginalString); } var fileHash = fullSourceName.ToString().GetHashCode(); // // Next, the crumbs. They should also be sorted in order, and we will need // a hash code for them too. // string[] crumbs = null; int crumbHash = 0; if (unsortedCrumbs == null) { crumbs = new string[0]; } else { crumbs = (from c in unsortedCrumbs orderby c select c).Distinct().ToArray(); StringBuilder crumbString = new StringBuilder(); foreach (var c in crumbs) { crumbString.Append(c); } crumbHash = crumbString.ToString().GetHashCode(); } /// /// Save the names of the files for a descriptor we will write out. /// KeyInfo result = new KeyInfo(); TraceHelpers.TraceInfo(25, "GetKey: Saving description lines"); result.DescriptionLines = (from f in rootfiles select f.OriginalString).ToArray(); result.ExtraQueryInfoLines = crumbs; /// /// Text for the query. There are strings like "generated_x" where x is a number. These get incremented each time they are used, /// so to protect the caching we need to swap those out with a dummy. /// TraceHelpers.TraceInfo(26, "GetKey: Pretty printing the query"); result.QueryText = FormattingQueryVisitor.Format(query); result.QueryText = result.QueryText.SwapOutWithUninqueString("\\<generated\\>_[0-9]+"); /// /// And the directory name - we use the first name of the file. /// TraceHelpers.TraceInfo(27, "GetKey: Getting the cache directory"); var fpathName = Path.GetFileNameWithoutExtension(rootfiles[0].PathAndQuery.SanitizedPathName(100)); result.CacheDirectory = new DirectoryInfo(CacheDirectory.FullName + "\\" + fileHash + " - " + treename + "-" + fpathName); /// /// Scan the files that we are input and find the oldest one there /// TraceHelpers.TraceInfo(28, "GetKey: calculating the most recent file dates"); result.OldestSourceFileDate = GetRecentFileDates(rootfiles, recheckDates, dateChecker).Max(); /// /// And now the file that the query should be cached in /// TraceHelpers.TraceInfo(29, "GetKey: Calculating query hash"); var queryHash = result.QueryText.GetHashCode(); TraceHelpers.TraceInfo(30, "GetKey: Calculating the input object hash"); var inputObjectHash = CalcObjectHash(inputObjects); string queryNameBase = string.Format(@"\\query {0}-inp{1}-crm{2}", queryHash.ToString(), inputObjectHash, crumbHash); result.RootFile = new FileInfo(result.CacheDirectory.FullName + queryNameBase + "_%%CYCLE%%.root"); // And a complete unique hash string. result.UniqueHashString = $"files{fileHash.ToString()}-query{queryHash.ToString()}-objs{inputObjectHash}-crm{crumbHash}"; TraceHelpers.TraceInfo(31, "GetKey: Done"); return(result); }
/// <summary> /// Return a QM function if we can find a list. /// </summary> /// <param name="queryModel"></param> /// <returns></returns> public IQMFunctionSource FindQMFunction(QueryModel queryModel) { var qmText = FormattingQueryVisitor.Format(queryModel); return(QMFunctions.Where(ff => ff.Matches(qmText)).FirstOrDefault()); }