Esempio n. 1
0
        internal NpgsqlDataReader GetReader(CommandBehavior cb)
        {
            CheckConnectionState();

            // Block the notification thread before writing anything to the wire.
            using (_connector.BlockNotificationThread())
            {
                State = CommandState.InProgress;

                NpgsqlDataReader reader;

                _connector.SetBackendCommandTimeout(CommandTimeout);

                if (_prepared == PrepareStatus.NeedsPrepare)
                {
                    PrepareInternal();
                }

                if (_prepared == PrepareStatus.NotPrepared)
                {
                    var commandText = GetCommandText();

                    // Write the Query message to the wire.
                    _connector.SendQuery(commandText);

                    // Tell to mediator what command is being sent.
                    if (_prepared == PrepareStatus.NotPrepared)
                    {
                        _connector.Mediator.SetSqlSent(commandText, NpgsqlMediator.SQLSentType.Simple);
                    }
                    else
                    {
                        _connector.Mediator.SetSqlSent(_preparedCommandText, NpgsqlMediator.SQLSentType.Execute);
                    }

                    reader = new NpgsqlDataReader(this, cb, _connector.BlockNotificationThread());

                    // For un-prepared statements, the first response is always a row description.
                    // For prepared statements, we may be recycling a row description from a previous Execute.
                    // TODO: This is the source of the inconsistency described in #357
                    reader.NextResult();
                    reader.UpdateOutputParameters();

                    if (
                        CommandType == CommandType.StoredProcedure
                        && reader.FieldCount == 1
                        && reader.GetDataTypeName(0) == "refcursor"
                    )
                    {
                        // When a function returns a sole column of refcursor, transparently
                        // FETCH ALL from every such cursor and return those results.
                        var sw = new StringWriter();

                        while (reader.Read())
                        {
                            sw.WriteLine(String.Format("FETCH ALL FROM \"{0}\";", reader.GetString(0)));
                        }

                        reader.Dispose();

                        var queryText = sw.ToString();

                        if (queryText == "")
                        {
                            queryText = ";";
                        }

                        // Passthrough the commandtimeout to the inner command, so user can also control its timeout.
                        // TODO: Check if there is a better way to handle that.

                        _connector.SendQuery(queryText);
                        reader = new NpgsqlDataReader(this, cb, _connector.BlockNotificationThread());
                        // For un-prepared statements, the first response is always a row description.
                        // For prepared statements, we may be recycling a row description from a previous Execute.
                        // TODO: This is the source of the inconsistency described in #357
                        reader.NextResultInternal();
                        reader.UpdateOutputParameters();
                    }
                }
                else
                {
                    // Bind the parameters, execute and sync
                    for (var i = 0; i < _parameters.Count; i++)
                        _parameters[i].Bind(_connector.NativeToBackendTypeConverterOptions);
                    _connector.SendBind(AnonymousPortal, _planName, _parameters, _resultFormatCodes);

                    _connector.SendExecute();
                    _connector.SendSync();

                    // Tell to mediator what command is being sent.
                    _connector.Mediator.SetSqlSent(_preparedCommandText, NpgsqlMediator.SQLSentType.Execute);

                    // Construct the return reader, possibly with a saved row description from Prepare().
                    reader = new NpgsqlDataReader(this, cb, _connector.BlockNotificationThread(), true, _currentRowDescription);
                    if (_currentRowDescription == null) {
                        reader.NextResultInternal();
                    }
                    reader.UpdateOutputParameters();
                }

                return reader;
            }
        }
Esempio n. 2
0
        internal NpgsqlDataReader GetReader(CommandBehavior cb)
        {
            CheckConnectionState();

            // Block the notification thread before writing anything to the wire.
            using (_connector.BlockNotificationThread())
            {
                IEnumerable<IServerResponseObject> responseEnum;
                NpgsqlDataReader reader;

                _connector.SetBackendCommandTimeout(CommandTimeout);

                if (_prepared == PrepareStatus.NeedsPrepare)
                {
                    PrepareInternal();
                }

                if (_prepared == PrepareStatus.NotPrepared)
                {
                    byte[] commandText = GetCommandText();

                    var query = new NpgsqlQuery(commandText);

                    // Write the Query message to the wire.
                    _connector.Query(query);

                    // Tell to mediator what command is being sent.
                    if (_prepared == PrepareStatus.NotPrepared)
                    {
                        _connector.Mediator.SetSqlSent(commandText, NpgsqlMediator.SQLSentType.Simple);
                    }
                    else
                    {
                        _connector.Mediator.SetSqlSent(_preparedCommandText, NpgsqlMediator.SQLSentType.Execute);
                    }

                    // Flush and wait for responses.
                    responseEnum = _connector.ProcessBackendResponsesEnum();

                    // Construct the return reader.
                    reader = new NpgsqlDataReader(
                        responseEnum,
                        cb,
                        this,
                        _connector.BlockNotificationThread()
                    );

                    if (
                        CommandType == CommandType.StoredProcedure
                        && reader.FieldCount == 1
                        && reader.GetDataTypeName(0) == "refcursor"
                    )
                    {
                        // When a function returns a sole column of refcursor, transparently
                        // FETCH ALL from every such cursor and return those results.
                        var sw = new StringWriter();

                        while (reader.Read())
                        {
                            sw.WriteLine("FETCH ALL FROM \"{0}\";", reader.GetString(0));
                        }

                        reader.Dispose();

                        var queryText = sw.ToString();

                        if (queryText == "")
                        {
                            queryText = ";";
                        }

                        // Passthrough the commandtimeout to the inner command, so user can also control its timeout.
                        // TODO: Check if there is a better way to handle that.

                        query = new NpgsqlQuery(queryText);

                        // Write the Query message to the wire.
                        _connector.Query(query);

                        // Flush and wait for responses.
                        responseEnum = _connector.ProcessBackendResponsesEnum();

                        // Construct the return reader.
                        reader = new NpgsqlDataReader(
                            responseEnum,
                            cb,
                            this,
                            _connector.BlockNotificationThread()
                        );
                    }
                }
                else
                {
                    // Update the Bind object with current parameter data as needed.
                    BindParameters();

                    // Write the Bind, Execute, and Sync message to the wire.
                    _connector.Bind(_bind);
                    _connector.Execute(_execute);
                    _connector.Sync();

                    // Tell to mediator what command is being sent.
                    _connector.Mediator.SetSqlSent(_preparedCommandText, NpgsqlMediator.SQLSentType.Execute);

                    // Flush and wait for responses.
                    responseEnum = _connector.ProcessBackendResponsesEnum();

                    // Construct the return reader, possibly with a saved row description from Prepare().
                    reader = new NpgsqlDataReader(
                        responseEnum,
                        cb,
                        this,
                        _connector.BlockNotificationThread(),
                        true,
                        _currentRowDescription
                    );
                }

                return reader;
            }
        }