internal ForwardsOnlyDataReader GetReader(CommandBehavior cb)
{
CheckConnectionState();
// Block the notification thread before writing anything to the wire.
using (m_Connector.BlockNotificationThread())
{
IEnumerable<IServerResponseObject> responseEnum;
ForwardsOnlyDataReader reader;
m_Connector.SetBackendCommandTimeout(CommandTimeout);
if (prepared == PrepareStatus.NeedsPrepare)
{
PrepareInternal();
}
if (prepared == PrepareStatus.NotPrepared || prepared == PrepareStatus.V2Prepared)
{
NpgsqlQuery query;
byte[] commandText = GetCommandText();
query = NpgsqlQuery.Create(m_Connector.BackendProtocolVersion, commandText);
// Write the Query message to the wire.
m_Connector.Query(query);
// Tell to mediator what command is being sent.
if (prepared == PrepareStatus.NotPrepared)
{
m_Connector.Mediator.SetSqlSent(commandText, NpgsqlMediator.SQLSentType.Simple);
}
else
{
m_Connector.Mediator.SetSqlSent(preparedCommandText, NpgsqlMediator.SQLSentType.Execute);
}
// Flush and wait for responses.
responseEnum = m_Connector.ProcessBackendResponsesEnum();
// Construct the return reader.
reader = new ForwardsOnlyDataReader(
responseEnum,
cb,
this,
m_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.
StringWriter sw = new StringWriter();
string queryText;
while (reader.Read())
{
sw.WriteLine("FETCH ALL FROM \"{0}\";", reader.GetString(0));
}
reader.Dispose();
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 = NpgsqlQuery.Create(m_Connector.BackendProtocolVersion, queryText);
// Write the Query message to the wire.
m_Connector.Query(query);
// Flush and wait for responses.
responseEnum = m_Connector.ProcessBackendResponsesEnum();
// Construct the return reader.
reader = new ForwardsOnlyDataReader(
responseEnum,
cb,
this,
m_Connector.BlockNotificationThread()
);
}
}
else
{
// Update the Bind object with current parameter data as needed.
BindParameters();
// Write the Bind, Execute, and Sync message to the wire.
m_Connector.Bind(bind);
m_Connector.Execute(execute);
m_Connector.Sync();
// Tell to mediator what command is being sent.
m_Connector.Mediator.SetSqlSent(preparedCommandText, NpgsqlMediator.SQLSentType.Execute);
// Flush and wait for responses.
responseEnum = m_Connector.ProcessBackendResponsesEnum();
// Construct the return reader, possibly with a saved row description from Prepare().
reader = new ForwardsOnlyDataReader(
responseEnum,
cb,
this,
m_Connector.BlockNotificationThread(),
true,
currentRowDescription
);
}
return reader;
}
}