Ejemplo n.º 1
0
        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;
            }
        }