internal Task WriteExecute(int maxRows, bool async) { // Note: non-empty portal currently not supported const int len = sizeof(byte) + // Message code sizeof(int) + // Length sizeof(byte) + // Null-terminated portal name (always empty for now) sizeof(int); // Max number of rows if (WriteBuffer.WriteSpaceLeft < len) { return(FlushAndWrite(maxRows, async)); } Write(maxRows); return(Task.CompletedTask); async Task FlushAndWrite(int maxRows, bool async) { await Flush(async); Debug.Assert(10 <= WriteBuffer.WriteSpaceLeft, $"Message of type {GetType().Name} has length 10 which is bigger than the buffer ({WriteBuffer.WriteSpaceLeft})"); Write(maxRows); } void Write(int maxRows) { WriteBuffer.WriteByte(FrontendMessageCode.Execute); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteByte(0); // Portal is always empty for now WriteBuffer.WriteInt32(maxRows); } }
internal Task WriteSync(bool async) { const int len = sizeof(byte) + // Message code sizeof(int); // Length if (WriteBuffer.WriteSpaceLeft < len) { return(FlushAndWrite(async)); } Write(); return(Task.CompletedTask); async Task FlushAndWrite(bool async) { await Flush(async); Debug.Assert(len <= WriteBuffer.WriteSpaceLeft, $"Message of type {GetType().Name} has length {len} which is bigger than the buffer ({WriteBuffer.WriteSpaceLeft})"); Write(); } void Write() { WriteBuffer.WriteByte(FrontendMessageCode.Sync); WriteBuffer.WriteInt32(len - 1); } }
internal async Task WriteSASLInitialResponse(string mechanism, byte[] initialResponse, bool async) { var len = sizeof(byte) + // Message code sizeof(int) + // Length PGUtil.UTF8Encoding.GetByteCount(mechanism) + sizeof(byte) + // Mechanism plus null terminator sizeof(int) + // Initial response length (initialResponse?.Length ?? 0); // Initial response payload if (WriteBuffer.WriteSpaceLeft < len) { await WriteBuffer.Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteString(mechanism); WriteBuffer.WriteByte(0); // null terminator if (initialResponse == null) { WriteBuffer.WriteInt32(-1); } else { WriteBuffer.WriteInt32(initialResponse.Length); WriteBuffer.WriteBytes(initialResponse); } }
internal void WriteStartup(Dictionary <string, string> parameters) { const int protocolVersion3 = 3 << 16; // 196608 var len = sizeof(int) + // Length sizeof(int) + // Protocol version sizeof(byte); // Trailing zero byte foreach (var kvp in parameters) { len += PGUtil.UTF8Encoding.GetByteCount(kvp.Key) + 1 + PGUtil.UTF8Encoding.GetByteCount(kvp.Value) + 1; } // Should really never happen, just in case if (len > WriteBuffer.Size) { throw new Exception("Startup message bigger than buffer"); } WriteBuffer.WriteInt32(len); WriteBuffer.WriteInt32(protocolVersion3); foreach (var kv in parameters) { WriteBuffer.WriteString(kv.Key); WriteBuffer.WriteByte(0); WriteBuffer.WriteString(kv.Value); WriteBuffer.WriteByte(0); } WriteBuffer.WriteByte(0); }
internal Task WriteClose(StatementOrPortal type, string name, bool async) { var len = sizeof(byte) + // Message code sizeof(int) + // Length sizeof(byte) + // Statement or portal name.Length + sizeof(byte); // Statement or portal name plus null terminator if (WriteBuffer.WriteSpaceLeft < 10) { return(FlushAndWrite(len, type, name, async)); } Write(len, type, name); return(Task.CompletedTask); async Task FlushAndWrite(int len, StatementOrPortal type, string name, bool async) { await Flush(async); Debug.Assert(len <= WriteBuffer.WriteSpaceLeft, $"Message of type {GetType().Name} has length {len} which is bigger than the buffer ({WriteBuffer.WriteSpaceLeft})"); Write(len, type, name); } void Write(int len, StatementOrPortal type, string name) { WriteBuffer.WriteByte(FrontendMessageCode.Close); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteByte((byte)type); WriteBuffer.WriteNullTerminatedString(name); } }
internal Task WriteDescribe(StatementOrPortal statementOrPortal, string name, bool async) { Debug.Assert(name.All(c => c < 128)); var len = sizeof(byte) + // Message code sizeof(int) + // Length sizeof(byte) + // Statement or portal (name.Length + 1); // Statement/portal name if (WriteBuffer.WriteSpaceLeft < len) { return(FlushAndWrite(len, statementOrPortal, name, async)); } Write(len, statementOrPortal, name); return(Task.CompletedTask); async Task FlushAndWrite(int len, StatementOrPortal statementOrPortal, string name, bool async) { await Flush(async); Debug.Assert(len <= WriteBuffer.WriteSpaceLeft, $"Message of type {GetType().Name} has length {len} which is bigger than the buffer ({WriteBuffer.WriteSpaceLeft})"); Write(len, statementOrPortal, name); } void Write(int len, StatementOrPortal statementOrPortal, string name) { WriteBuffer.WriteByte(FrontendMessageCode.Describe); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteByte((byte)statementOrPortal); WriteBuffer.WriteNullTerminatedString(name); } }
internal async Task WriteParse(string sql, string statementName, List <NpgsqlParameter> inputParameters, bool async) { Debug.Assert(statementName.All(c => c < 128)); int queryByteLen; try { queryByteLen = TextEncoding.GetByteCount(sql); } catch (Exception e) { Break(e); throw; } if (WriteBuffer.WriteSpaceLeft < 1 + 4 + statementName.Length + 1) { await Flush(async); } var messageLength = sizeof(byte) + // Message code sizeof(int) + // Length statementName.Length + // Statement name sizeof(byte) + // Null terminator for the statement name queryByteLen + sizeof(byte) + // SQL query length plus null terminator sizeof(ushort) + // Number of parameters inputParameters.Count * sizeof(int); // Parameter OIDs WriteBuffer.WriteByte(FrontendMessageCode.Parse); WriteBuffer.WriteInt32(messageLength - 1); WriteBuffer.WriteNullTerminatedString(statementName); await WriteBuffer.WriteString(sql, queryByteLen, async); if (WriteBuffer.WriteSpaceLeft < 1 + 2) { await Flush(async); } WriteBuffer.WriteByte(0); // Null terminator for the query WriteBuffer.WriteUInt16((ushort)inputParameters.Count); foreach (var p in inputParameters) { if (WriteBuffer.WriteSpaceLeft < 4) { await Flush(async); } WriteBuffer.WriteInt32((int)p.Handler !.PostgresType.OID); } }
void EnsureDataMessage() { if (_writingDataMsg) { return; } Contract.Assert(_buf.WritePosition == 0); _buf.WriteByte((byte)BackendMessageCode.CopyData); // Leave space for the message length _buf.WriteInt32(0); _writingDataMsg = true; }
internal void WriteTerminate() { const int len = sizeof(byte) + // Message code sizeof(int); // Length if (WriteBuffer.WriteSpaceLeft < len) { Flush(false).GetAwaiter().GetResult(); } WriteBuffer.WriteByte(FrontendMessageCode.Terminate); WriteBuffer.WriteInt32(len - 1); }
internal async Task WriteCopyDone(bool async) { const int len = sizeof(byte) + // Message code sizeof(int); // Length if (WriteBuffer.WriteSpaceLeft < len) { await Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.CopyDone); WriteBuffer.WriteInt32(len - 1); }
public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); if (!CanWrite) { throw new InvalidOperationException("Stream not open for writing"); } if (count == 0) { return; } EnsureDataMessage(); if (count <= _writeBuf.WriteSpaceLeft) { _writeBuf.WriteBytes(buffer, offset, count); return; } try { // Value is too big. Flush whatever is in the buffer, then write a new CopyData // directly with the buffer. Flush(); _writeBuf.WriteByte((byte)BackendMessageCode.CopyData); _writeBuf.WriteInt32(count + 4); _writeBuf.Flush(); _writeBuf.DirectWrite(buffer, offset, count); EnsureDataMessage(); } catch { _connector.Break(); Cleanup(); throw; } }
internal async Task WriteCopyFail(bool async) { // Note: error message not supported for now const int len = sizeof(byte) + // Message code sizeof(int) + // Length sizeof(byte); // Error message is always empty (only a null terminator) if (WriteBuffer.WriteSpaceLeft < len) { await Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.CopyFail); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteByte(0); // Error message is always empty (only a null terminator) }
internal async Task WritePassword(byte[] payload, int offset, int count, bool async) { if (WriteBuffer.WriteSpaceLeft < sizeof(byte) + sizeof(int)) { await WriteBuffer.Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(sizeof(int) + count); if (count <= WriteBuffer.WriteSpaceLeft) { // The entire array fits in our WriteBuffer, copy it into the WriteBuffer as usual. WriteBuffer.WriteBytes(payload, offset, count); return; } await WriteBuffer.Flush(async); await WriteBuffer.DirectWrite(payload, offset, count, async); }
internal async Task WritePassword(byte[] payload, int offset, int count, bool async, CancellationToken cancellationToken = default) { if (WriteBuffer.WriteSpaceLeft < sizeof(byte) + sizeof(int)) { await WriteBuffer.Flush(async, cancellationToken); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(sizeof(int) + count); if (count <= WriteBuffer.WriteSpaceLeft) { // The entire array fits in our WriteBuffer, copy it into the WriteBuffer as usual. WriteBuffer.WriteBytes(payload, offset, count); return; } await WriteBuffer.Flush(async, cancellationToken); await WriteBuffer.DirectWrite(new ReadOnlyMemory <byte>(payload, offset, count), async, cancellationToken); }
internal async Task WriteQuery(string sql, bool async) { var queryByteLen = TextEncoding.GetByteCount(sql); if (WriteBuffer.WriteSpaceLeft < 1 + 4) { await Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Query); WriteBuffer.WriteInt32( sizeof(int) + // Message length (including self excluding code) queryByteLen + // Query byte length sizeof(byte)); // Null terminator await WriteBuffer.WriteString(sql, queryByteLen, async); if (WriteBuffer.WriteSpaceLeft < 1) { await Flush(async); } WriteBuffer.WriteByte(0); // Null terminator }
internal async Task WriteBind( List <NpgsqlParameter> inputParameters, string portal, string statement, bool allResultTypesAreUnknown, bool[]?unknownResultTypeList, bool async) { Debug.Assert(statement.All(c => c < 128)); Debug.Assert(portal.All(c => c < 128)); var headerLength = sizeof(byte) + // Message code sizeof(int) + // Message length sizeof(byte) + // Portal is always empty (only a null terminator) statement.Length + sizeof(byte) + // Statement name plus null terminator sizeof(short); // Number of parameter format codes that follow if (WriteBuffer.WriteSpaceLeft < headerLength) { Debug.Assert(WriteBuffer.Size >= headerLength, "Write buffer too small for Bind header"); await Flush(async); } var formatCodesSum = 0; var paramsLength = 0; foreach (var p in inputParameters) { formatCodesSum += (int)p.FormatCode; p.LengthCache?.Rewind(); paramsLength += p.ValidateAndGetLength(); } var formatCodeListLength = formatCodesSum == 0 ? 0 : formatCodesSum == inputParameters.Count ? 1 : inputParameters.Count; var messageLength = headerLength + sizeof(short) * formatCodeListLength + // List of format codes sizeof(short) + // Number of parameters sizeof(int) * inputParameters.Count + // Parameter lengths paramsLength + // Parameter values sizeof(short) + // Number of result format codes sizeof(short) * (unknownResultTypeList?.Length ?? 1); // Result format codes WriteBuffer.WriteByte(FrontendMessageCode.Bind); WriteBuffer.WriteInt32(messageLength - 1); Debug.Assert(portal == string.Empty); WriteBuffer.WriteByte(0); // Portal is always empty WriteBuffer.WriteNullTerminatedString(statement); WriteBuffer.WriteInt16(formatCodeListLength); // 0 length implicitly means all-text, 1 means all-binary, >1 means mix-and-match if (formatCodeListLength == 1) { if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16((short)FormatCode.Binary); } else if (formatCodeListLength > 1) { foreach (var p in inputParameters) { if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16((short)p.FormatCode); } } if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16(inputParameters.Count); foreach (var param in inputParameters) { param.LengthCache?.Rewind(); await param.WriteWithLength(WriteBuffer, async); } if (unknownResultTypeList != null) { if (WriteBuffer.WriteSpaceLeft < 2 + unknownResultTypeList.Length * 2) { await Flush(async); } WriteBuffer.WriteInt16(unknownResultTypeList.Length); foreach (var t in unknownResultTypeList) { WriteBuffer.WriteInt16(t ? 0 : 1); } } else { if (WriteBuffer.WriteSpaceLeft < 4) { await Flush(async); } WriteBuffer.WriteInt16(1); WriteBuffer.WriteInt16(allResultTypesAreUnknown ? 0 : 1); } }