// Protected implementation of Dispose pattern. protected virtual void Dispose(bool dispose) { if (_state == TransactionState.Disposed) { return; } ENSURE(_state != TransactionState.Disposed, "transaction must be active before call Done"); // clean snapshots if there is no commit/rollback if (_state == TransactionState.Active && _snapshots.Count > 0) { // release writable snapshots foreach (var snapshot in _snapshots.Values.Where(x => x.Mode == LockMode.Write)) { // discard all dirty pages _disk.DiscardDirtyPages(snapshot.GetWritablePages(true, true).Select(x => x.Buffer)); // discard all clean pages _disk.DiscardCleanPages(snapshot.GetWritablePages(false, true).Select(x => x.Buffer)); } // release buffers in read-only snaphosts foreach (var snapshot in _snapshots.Values.Where(x => x.Mode == LockMode.Read)) { foreach (var page in snapshot.LocalPages) { page.Buffer.Release(); } snapshot.CollectionPage?.Buffer.Release(); } } _reader.Dispose(); _state = TransactionState.Disposed; if (!dispose) { // Remove transaction monitor's dictionary _monitor.ReleaseTransaction(this); } }
/// <summary> /// Run query definition into engine. Execute optimization to get query planner /// </summary> internal BsonDataReader ExecuteQuery(bool executionPlan) { // get current transaction (if contains a explicit transaction) or a query-only transaction var transaction = _monitor.GetTransaction(true, true, out var isNew); transaction.OpenCursors.Add(_cursor); // return new BsonDataReader with IEnumerable source return(new BsonDataReader(RunQuery(), _collection)); IEnumerable <BsonDocument> RunQuery() { var snapshot = 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) { _monitor.ReleaseTransaction(transaction); } 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) { _monitor.ReleaseTransaction(transaction); } 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, _sortDisk, _pragmas); try { // start cursor elapsed timer _cursor.Elapsed.Start(); // call safepoint just before return each document 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) { _monitor.ReleaseTransaction(transaction); } } }; }