/// <summary> /// Run query definition into engine. Execute optimization to get query planner /// </summary> internal async Task <BsonDataReader> ExecuteQuery(bool executionPlan) { // get current transaction (if contains a explicit transaction) or a query-only transaction var isNew = !_engine.HasTransaction(); var transaction = await _engine.GetTransaction(); //transaction.OpenCursors.Add(_cursor); // return new BsonDataReader with IEnumerable source return(await BsonDataReader.CreateAsync(RunQuery(), _collection)); async IAsyncEnumerable <BsonDocument> RunQuery() { var snapshot = await transaction.CreateSnapshot(_query.ForUpdate?LockMode.Write : LockMode.Read, _collection, false); // no collection, no documents if (snapshot.CollectionPage == null && _source == null) { // if query use Source (*) need runs with empty data source if (_query.Select.UseSource) { yield return(_query.Select.ExecuteScalar(_pragmas.Collation).AsDocument); } transaction.OpenCursors.Remove(_cursor); if (isNew) { _engine.ClearTransaction(); } yield break; } // execute optimization before run query (will fill missing _query properties instance) var optimizer = new QueryOptimization(snapshot, _query, _source, _pragmas.Collation); var queryPlan = optimizer.ProcessQuery(); // if execution is just to get explan plan, return as single document result if (executionPlan) { yield return(queryPlan.GetExecutionPlan()); transaction.OpenCursors.Remove(_cursor); if (isNew) { _engine.ClearTransaction(); } yield break; } // get node list from query - distinct by dataBlock (avoid duplicate) var nodes = queryPlan.Index.Run(snapshot.CollectionPage, new IndexService(snapshot, _pragmas.Collation)); // get current query pipe: normal or groupby pipe var pipe = queryPlan.GetPipe(transaction, snapshot, _pragmas); try { // start cursor elapsed timer _cursor.Elapsed.Start(); // call safepoint just before return each document await foreach (var doc in pipe.Pipe(nodes, queryPlan)) { _cursor.Fetched++; _cursor.Elapsed.Stop(); yield return(doc); if (transaction.State != TransactionState.Active) { throw new LiteException(0, $"There is no more active transaction for this cursor: {_cursor.Query.ToSQL(_cursor.Collection)}"); } _cursor.Elapsed.Start(); } } finally { // stop cursor elapsed _cursor.Elapsed.Stop(); transaction.OpenCursors.Remove(_cursor); if (isNew) { _engine.ClearTransaction(); } } }; }