예제 #1
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            Debug.Assert(_members != null);

            var composite = (T)value;

            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(_members.Count);

            foreach (var fieldDescriptor in _members)
            {
                var fieldHandler = fieldDescriptor.Handler;
                var fieldValue   = fieldDescriptor.GetValue(composite);

                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }

                buf.WriteUInt32(fieldDescriptor.OID);
                await fieldHandler.WriteWithLength(fieldValue, buf, lengthCache, null, async, cancellationToken);
            }
        }
예제 #2
0
        async Task WriteBitArray(BitArray bitArray, WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            // Initial bitlength byte
            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(bitArray.Length);

            var byteLen = (bitArray.Length + 7) / 8;
            var pos     = 0;

            while (true)
            {
                var endPos = pos + Math.Min(byteLen - pos, buf.WriteSpaceLeft);
                for (; pos < endPos; pos++)
                {
                    var bitPos = pos * 8;
                    var b      = 0;
                    for (var i = 0; i < Math.Min(8, bitArray.Length - bitPos); i++)
                    {
                        b += (bitArray[bitPos + i] ? 1 : 0) << (8 - i - 1);
                    }
                    buf.WriteByte((byte)b);
                }

                if (pos == byteLen)
                {
                    return;
                }
                await buf.Flush(async, cancellationToken);
            }
        }
예제 #3
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var vector = (NpgsqlTsVector)value;

            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(vector.Count);

            foreach (var lexeme in vector)
            {
                if (buf.WriteSpaceLeft < MaxSingleLexemeBytes)
                {
                    await buf.Flush(async, cancellationToken);
                }

                buf.WriteString(lexeme.Text);
                buf.WriteByte(0);
                buf.WriteInt16(lexeme.Count);
                for (var i = 0; i < lexeme.Count; i++)
                {
                    buf.WriteInt16(lexeme[i].Value);
                }
            }
        }
예제 #4
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var query     = (NpgsqlTsQuery)value;
            var numTokens = GetTokenCount(query);

            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(numTokens);

            if (numTokens == 0)
            {
                return;
            }

            _stack.Push(query);

            while (_stack.Count > 0)
            {
                if (buf.WriteSpaceLeft < 2)
                {
                    await buf.Flush(async, cancellationToken);
                }

                if (_stack.Peek().Kind == NpgsqlTsQuery.NodeKind.Lexeme && buf.WriteSpaceLeft < MaxSingleTokenBytes)
                {
                    await buf.Flush(async, cancellationToken);
                }

                var node = _stack.Pop();
                buf.WriteByte(node.Kind == NpgsqlTsQuery.NodeKind.Lexeme ? (byte)1 : (byte)2);
                if (node.Kind != NpgsqlTsQuery.NodeKind.Lexeme)
                {
                    buf.WriteByte((byte)node.Kind);
                    if (node.Kind == NpgsqlTsQuery.NodeKind.Not)
                    {
                        _stack.Push(((NpgsqlTsQueryNot)node).Child);
                    }
                    else
                    {
                        _stack.Push(((NpgsqlTsQueryBinOp)node).Right);
                        _stack.Push(((NpgsqlTsQueryBinOp)node).Left);
                    }
                }
                else
                {
                    var lexemeNode = (NpgsqlTsQueryLexeme)node;
                    buf.WriteByte((byte)lexemeNode.Weights);
                    buf.WriteByte(lexemeNode.IsPrefixSearch ? (byte)1 : (byte)0);
                    buf.WriteString(lexemeNode.Text);
                    buf.WriteByte(0);
                }
            }

            _stack.Clear();
        }
예제 #5
0
        async Task WriteString(string str, WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            // Initial bitlength byte
            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(str.Length);

            var pos     = 0;
            var byteLen = (str.Length + 7) / 8;
            var bytePos = 0;

            while (true)
            {
                var endBytePos = bytePos + Math.Min(byteLen - bytePos - 1, buf.WriteSpaceLeft);

                for (; bytePos < endBytePos; bytePos++)
                {
                    var b = 0;
                    b += (str[pos++] - '0') << 7;
                    b += (str[pos++] - '0') << 6;
                    b += (str[pos++] - '0') << 5;
                    b += (str[pos++] - '0') << 4;
                    b += (str[pos++] - '0') << 3;
                    b += (str[pos++] - '0') << 2;
                    b += (str[pos++] - '0') << 1;
                    b += (str[pos++] - '0');
                    buf.WriteByte((byte)b);
                }

                if (bytePos >= byteLen - 1)
                {
                    break;
                }
                await buf.Flush(async, cancellationToken);
            }

            if (pos < str.Length)
            {
                if (buf.WriteSpaceLeft < 1)
                {
                    await buf.Flush(async, cancellationToken);
                }

                var remainder = str.Length - pos;
                var lastChunk = 0;
                for (var i = 7; i >= 8 - remainder; i--)
                {
                    lastChunk += (str[pos++] - '0') << i;
                }
                buf.WriteByte((byte)lastChunk);
            }
        }
예제 #6
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, [CanBeNull] NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            ArraySegment <byte> segment;

            if (value is ArraySegment <byte> )
            {
                segment = (ArraySegment <byte>)value;
                if (!(parameter == null || parameter.Size <= 0 || parameter.Size >= segment.Count))
                {
                    segment = new ArraySegment <byte>(segment.Array, segment.Offset, parameter.Size);
                }
            }
            else
            {
                var array = (byte[])value;
                var len   = parameter == null || parameter.Size <= 0 || parameter.Size >= array.Length
                    ? array.Length
                    : parameter.Size;
                segment = new ArraySegment <byte>(array, 0, len);
            }

            // The entire segment fits in our buffer, copy it as usual.
            if (segment.Count <= buf.WriteSpaceLeft)
            {
                buf.WriteBytes(segment.Array, segment.Offset, segment.Count);
                return;
            }

            // The segment is larger than our buffer. Flush whatever is currently in the buffer and
            // write the array directly to the socket.
            await buf.Flush(async, cancellationToken);

            buf.DirectWrite(segment.Array, segment.Offset, segment.Count);
        }
예제 #7
0
    internal async Task WriteSASLInitialResponse(string mechanism, byte[] initialResponse, bool async, CancellationToken cancellationToken = default)
    {
        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, cancellationToken);
        }

        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);
        }
    }
예제 #8
0
        async Task AuthenticateMD5(string username, byte[] salt, bool async)
        {
            var passwd = Settings.Password;

            // No password was provided. Attempt to pull the password from the pgpass file.
            if (passwd == null)
            {
                var matchingEntry = PgPassFile.Load(Settings.Passfile)?.GetFirstMatchingEntry(Settings.Host, Settings.Port, Settings.Database, Settings.Username);
                if (matchingEntry != null)
                {
                    Log.Trace("Taking password from pgpass file");
                    passwd = matchingEntry.Password;
                }
            }

            if (passwd == null)
            {
                throw new NpgsqlException("No password has been provided but the backend requires one (in MD5)");
            }

            await PasswordMessage
            .CreateMD5(passwd, username, salt)
            .Write(WriteBuffer, async);

            await WriteBuffer.Flush(async);

            await ReadExpecting <AuthenticationRequestMessage>(async);
        }
예제 #9
0
 async Task WriteBool(bool b, WriteBuffer buf, bool async, CancellationToken cancellationToken)
 {
     if (buf.WriteSpaceLeft < 5)
     {
         await buf.Flush(async, cancellationToken);
     }
     buf.WriteInt32(1);
     buf.WriteByte(b ? (byte)0x80 : (byte)0);
 }
예제 #10
0
 protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                     bool async, CancellationToken cancellationToken)
 {
     if (buf.WriteSpaceLeft < 1)
     {
         await buf.Flush(async, cancellationToken);
     }
     buf.WriteByte(JsonbProtocolVersion);
     await base.Write(value, buf, lengthCache, parameter, async, cancellationToken);
 }
예제 #11
0
        internal override async Task Write(WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            Debug.Assert(Statement != null && Statement.All(c => c < 128));

            var queryByteLen = _encoding.GetByteCount(Query);

            if (buf.WriteSpaceLeft < 1 + 4 + Statement.Length + 1)
            {
                await buf.Flush(async, cancellationToken);
            }

            var messageLength =
                1 +                         // Message code
                4 +                         // Length
                Statement.Length +
                1 +                         // Null terminator
                queryByteLen +
                1 +                         // Null terminator
                2 +                         // Number of parameters
                ParameterTypeOIDs.Count * 4;

            buf.WriteByte(Code);
            buf.WriteInt32(messageLength - 1);
            buf.WriteNullTerminatedString(Statement);

            await buf.WriteString(Query, queryByteLen, async, cancellationToken);

            if (buf.WriteSpaceLeft < 1 + 2)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteByte(0); // Null terminator for the query
            buf.WriteInt16((short)ParameterTypeOIDs.Count);

            foreach (var t in ParameterTypeOIDs)
            {
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32((int)t);
            }
        }
예제 #12
0
        internal override async Task Write(WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            if (buf.WriteSpaceLeft < 1 + 5)
            {
                await buf.Flush(async);
            }
            buf.WriteByte(Code);
            buf.WriteInt32(4 + PayloadLength);

            if (PayloadLength <= buf.WriteSpaceLeft)
            {
                // The entire array fits in our buffer, copy it into the buffer as usual.
                buf.WriteBytes(Payload, PayloadOffset, Payload.Length);
                return;
            }

            await buf.Flush(async);

            buf.DirectWrite(Payload, PayloadOffset, PayloadLength);
        }
예제 #13
0
        internal override async Task Write(WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            if (buf.WriteSpaceLeft < 1 + 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            var queryByteLen = _encoding.GetByteCount(_query);

            buf.WriteByte(Code);
            buf.WriteInt32(4 +            // Message length (including self excluding code)
                           queryByteLen + // Query byte length
                           1);            // Null terminator

            await buf.WriteString(_query, queryByteLen, async, cancellationToken);

            if (buf.WriteSpaceLeft < 1)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteByte(0);
        }
예제 #14
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var polygon = (NpgsqlPolygon)value;

            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(polygon.Count);

            foreach (var p in polygon)
            {
                if (buf.WriteSpaceLeft < 16)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteDouble(p.X);
                buf.WriteDouble(p.Y);
            }
        }
예제 #15
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var path = (NpgsqlPath)value;

            if (buf.WriteSpaceLeft < 5)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteByte((byte)(path.Open ? 0 : 1));
            buf.WriteInt32(path.Count);

            foreach (var p in path)
            {
                if (buf.WriteSpaceLeft < 16)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteDouble(p.X);
                buf.WriteDouble(p.Y);
            }
        }
예제 #16
0
        static PregeneratedMessage BuildQuery(string query)
        {
            Contract.Requires(query != null && query.All(c => c < 128));

            var totalLen = 5 + query.Length;
            var ms       = new MemoryStream(totalLen);

            _tempBuf.Underlying = ms;
            _tempQuery.Populate(query);
            _tempQuery.Write(_tempBuf);
            _tempBuf.Flush();
            return(new PregeneratedMessage(ms.ToArray(), _tempQuery.ToString()));
        }
예제 #17
0
        async Task WriteBitVector32(BitVector32 bitVector, WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            if (buf.WriteSpaceLeft < 8)
            {
                await buf.Flush(async, cancellationToken);
            }

            if (bitVector.Data == 0)
            {
                buf.WriteInt32(0);
            }
            else
            {
                buf.WriteInt32(32);
                buf.WriteInt32(bitVector.Data);
            }
        }
예제 #18
0
    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);
    }
예제 #19
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var asDict = (IDictionary <string, string>)value;

            if (buf.WriteSpaceLeft < 4)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteInt32(asDict.Count);
            if (asDict.Count == 0)
            {
                return;
            }

            foreach (var kv in asDict)
            {
                await _textHandler.WriteWithLength(kv.Key, buf, lengthCache, parameter, async, cancellationToken);

                await _textHandler.WriteWithLength(kv.Value, buf, lengthCache, parameter, async, cancellationToken);
            }
        }
예제 #20
0
        protected override async Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                                            bool async, CancellationToken cancellationToken)
        {
            var range = (NpgsqlRange <TElement>)value;

            if (buf.WriteSpaceLeft < 1)
            {
                await buf.Flush(async, cancellationToken);
            }
            buf.WriteByte((byte)range.Flags);
            if (range.IsEmpty)
            {
                return;
            }
            if (!range.LowerBoundInfinite)
            {
                await ElementHandler.WriteWithLength(range.LowerBound, buf, lengthCache, null, async, cancellationToken);
            }
            if (!range.UpperBoundInfinite)
            {
                await ElementHandler.WriteWithLength(range.UpperBound, buf, lengthCache, null, async, cancellationToken);
            }
        }
예제 #21
0
 internal Task Flush(bool async, CancellationToken cancellationToken = default) => WriteBuffer.Flush(async, cancellationToken);
예제 #22
0
 internal void Flush() => WriteBuffer.Flush(false).GetAwaiter().GetResult();
예제 #23
0
파일: BindMessage.cs 프로젝트: zoroz/npgsql
        internal override async Task Write(WriteBuffer buf, bool async, CancellationToken cancellationToken)
        {
            Debug.Assert(Statement != null && Statement.All(c => c < 128));
            Debug.Assert(Portal != null && Portal.All(c => c < 128));

            var headerLength =
                1 +                        // Message code
                4 +                        // Message length
                1 +                        // Portal is always empty (only a null terminator)
                Statement.Length + 1 +
                2;                         // Number of parameter format codes that follow

            if (buf.WriteSpaceLeft < headerLength)
            {
                Debug.Assert(buf.Size >= headerLength, "Buffer too small for Bind header");
                await buf.Flush(async, cancellationToken);
            }

            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 +
                                2 * formatCodeListLength +                // List of format codes
                                2 +                                       // Number of parameters
                                4 * InputParameters.Count +               // Parameter lengths
                                paramsLength +                            // Parameter values
                                2 +                                       // Number of result format codes
                                2 * (UnknownResultTypeList?.Length ?? 1); // Result format codes

            buf.WriteByte(Code);
            buf.WriteInt32(messageLength - 1);
            Debug.Assert(Portal == string.Empty);
            buf.WriteByte(0);  // Portal is always empty

            buf.WriteNullTerminatedString(Statement);
            buf.WriteInt16(formatCodeListLength);

            // 0 length implicitly means all-text, 1 means all-binary, >1 means mix-and-match
            if (formatCodeListLength == 1)
            {
                if (buf.WriteSpaceLeft < 2)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt16((short)FormatCode.Binary);
            }
            else if (formatCodeListLength > 1)
            {
                foreach (NpgsqlParameter p in InputParameters)
                {
                    if (buf.WriteSpaceLeft < 2)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteInt16((short)p.FormatCode);
                }
            }

            if (buf.WriteSpaceLeft < 2)
            {
                await buf.Flush(async, cancellationToken);
            }

            buf.WriteInt16(InputParameters.Count);

            foreach (var param in InputParameters)
            {
                param.LengthCache?.Rewind();
                await param.WriteWithLength(buf, async, cancellationToken);
            }

            if (UnknownResultTypeList != null)
            {
                if (buf.WriteSpaceLeft < 2 + UnknownResultTypeList.Length * 2)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt16(UnknownResultTypeList.Length);
                foreach (var t in UnknownResultTypeList)
                {
                    buf.WriteInt16(t ? 0 : 1);
                }
            }
            else
            {
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt16(1);
                buf.WriteInt16(AllResultTypesAreUnknown ? 0 : 1);
            }
        }
예제 #24
0
        async Task Write(PostgisGeometry geom, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter,
                         bool async, CancellationToken cancellationToken)
        {
            // Common header
            if (geom.SRID == 0)
            {
                if (buf.WriteSpaceLeft < 5)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteByte(0); // We choose to ouput only XDR structure
                buf.WriteInt32((int)geom.Identifier);
            }
            else
            {
                if (buf.WriteSpaceLeft < 9)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteByte(0);
                buf.WriteInt32((int)((uint)geom.Identifier | (uint)EwkbModifiers.HasSRID));
                buf.WriteInt32((int)geom.SRID);
            }

            switch (geom.Identifier)
            {
            case WkbIdentifier.Point:
                if (buf.WriteSpaceLeft < 16)
                {
                    await buf.Flush(async, cancellationToken);
                }
                var p = (PostgisPoint)geom;
                buf.WriteDouble(p.X);
                buf.WriteDouble(p.Y);
                return;

            case WkbIdentifier.LineString:
                var l = (PostgisLineString)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(l.PointCount);
                for (var ipts = 0; ipts < l.PointCount; ipts++)
                {
                    if (buf.WriteSpaceLeft < 16)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteDouble(l[ipts].X);
                    buf.WriteDouble(l[ipts].Y);
                }
                return;

            case WkbIdentifier.Polygon:
                var pol = (PostgisPolygon)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(pol.RingCount);
                for (var irng = 0; irng < pol.RingCount; irng++)
                {
                    if (buf.WriteSpaceLeft < 4)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteInt32(pol[irng].Length);
                    for (var ipts = 0; ipts < pol[irng].Length; ipts++)
                    {
                        if (buf.WriteSpaceLeft < 16)
                        {
                            await buf.Flush(async, cancellationToken);
                        }
                        buf.WriteDouble(pol[irng][ipts].X);
                        buf.WriteDouble(pol[irng][ipts].Y);
                    }
                }
                return;

            case WkbIdentifier.MultiPoint:
                var mp = (PostgisMultiPoint)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(mp.PointCount);
                for (var ipts = 0; ipts < mp.PointCount; ipts++)
                {
                    if (buf.WriteSpaceLeft < 21)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteByte(0);
                    buf.WriteInt32((int)WkbIdentifier.Point);
                    buf.WriteDouble(mp[ipts].X);
                    buf.WriteDouble(mp[ipts].Y);
                }
                return;

            case WkbIdentifier.MultiLineString:
                var ml = (PostgisMultiLineString)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(ml.LineCount);
                for (var irng = 0; irng < ml.LineCount; irng++)
                {
                    if (buf.WriteSpaceLeft < 9)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteByte(0);
                    buf.WriteInt32((int)WkbIdentifier.LineString);
                    buf.WriteInt32(ml[irng].PointCount);
                    for (var ipts = 0; ipts < ml[irng].PointCount; ipts++)
                    {
                        if (buf.WriteSpaceLeft < 16)
                        {
                            await buf.Flush(async, cancellationToken);
                        }
                        buf.WriteDouble(ml[irng][ipts].X);
                        buf.WriteDouble(ml[irng][ipts].Y);
                    }
                }
                return;

            case WkbIdentifier.MultiPolygon:
                var mpl = (PostgisMultiPolygon)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(mpl.PolygonCount);
                for (var ipol = 0; ipol < mpl.PolygonCount; ipol++)
                {
                    if (buf.WriteSpaceLeft < 9)
                    {
                        await buf.Flush(async, cancellationToken);
                    }
                    buf.WriteByte(0);
                    buf.WriteInt32((int)WkbIdentifier.Polygon);
                    buf.WriteInt32(mpl[ipol].RingCount);
                    for (var irng = 0; irng < mpl[ipol].RingCount; irng++)
                    {
                        if (buf.WriteSpaceLeft < 4)
                        {
                            await buf.Flush(async, cancellationToken);
                        }
                        buf.WriteInt32(mpl[ipol][irng].Length);
                        for (var ipts = 0; ipts < mpl[ipol][irng].Length; ipts++)
                        {
                            if (buf.WriteSpaceLeft < 16)
                            {
                                await buf.Flush(async, cancellationToken);
                            }
                            buf.WriteDouble(mpl[ipol][irng][ipts].X);
                            buf.WriteDouble(mpl[ipol][irng][ipts].Y);
                        }
                    }
                }
                return;

            case WkbIdentifier.GeometryCollection:
                var coll = (PostgisGeometryCollection)geom;
                if (buf.WriteSpaceLeft < 4)
                {
                    await buf.Flush(async, cancellationToken);
                }
                buf.WriteInt32(coll.GeometryCount);

                foreach (var x in coll)
                {
                    await Write(x, buf, lengthCache, null, async, cancellationToken);
                }
                return;

            default:
                throw new InvalidOperationException("Unknown Postgis identifier.");
            }
        }