예제 #1
0
        /// <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();
                    }
                }
            };
        }