async ValueTask <NpgsqlDataReader> Execute(CommandBehavior behavior, bool async, CancellationToken cancellationToken) { ValidateParameters(); var connector = Connection.Connector; Debug.Assert(connector != null); if (_isParsed) { if (_connectorPreparedOn != null && _connectorPreparedOn != Connection.Connector) { // The command was prepared, but since then the connector has changed. Detach all prepared statements. foreach (var s in _statements) { s.PreparedStatement = null; } _connectorPreparedOn = null; } } else { ProcessRawQuery(); } State = CommandState.InProgress; try { Log.ExecuteCommand(connector.Id, this); Task sendTask; // If a cancellation is in progress, wait for it to "complete" before proceeding (#615) lock (connector.CancelLock) { } connector.UserTimeout = CommandTimeout * 1000; if ((behavior & CommandBehavior.SchemaOnly) == 0) { if (connector.Settings.MaxAutoPrepare > 0) { foreach (var statement in _statements) { // If this statement isn't prepared, see if it gets implicitly prepared. // Note that this may return null (not enough usages for automatic preparation). if (!statement.IsPrepared) { statement.PreparedStatement = connector.PreparedStatementManager.TryGetAutoPrepared(statement); } if (statement.PreparedStatement != null) { statement.PreparedStatement.LastUsed = DateTime.UtcNow; } } _connectorPreparedOn = connector; } // We do not wait for the entire send to complete before proceeding to reading - // the sending continues in parallel with the user's reading. Waiting for the // entire send to complete would trigger a deadlock for multistatement commands, // where PostgreSQL sends large results for the first statement, while we're sending large // parameter data for the second. See #641. // Instead, all sends for non-first statements and for non-first buffers are performed // asynchronously (even if the user requested sync), in a special synchronization context // to prevents a dependency on the thread pool (which would also trigger deadlocks). // The WriteBuffer notifies this command when the first buffer flush occurs, so that the // send functions can switch to the special async mode when needed. sendTask = SendExecute(async, cancellationToken); } else { sendTask = SendExecuteSchemaOnly(async, cancellationToken); } // The following is a hack. It raises an exception if one was thrown in the first phases // of the send (i.e. in parts of the send that executed synchronously). Exceptions may // still happen later and aren't properly handled. See #1323. if (sendTask.IsFaulted) { sendTask.GetAwaiter().GetResult(); } var reader = new NpgsqlDataReader(this, behavior, _statements, sendTask); if (async) { await reader.NextResultAsync(cancellationToken); } else { reader.NextResult(); } connector.CurrentReader = reader; return(reader); } catch { State = CommandState.Idle; throw; } }