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;
            }
        }