Summary

Class:CBAM.SQL.PostgreSQL.Implementation.DescribeMessage
Assembly:CBAM.SQL.PostgreSQL.Implementation
File(s):/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL.Implementation/Protocol.Writing.cs
Covered lines:10
Uncovered lines:2
Coverable lines:12
Total lines:784
Line coverage:83.3%
Branch coverage:100%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.cctor()100%0%
.ctor(...)101%0%
CalculateBufferSize(...)101%0%
WriteMessageToBuffer(...)201%1%

File(s)

/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL.Implementation/Protocol.Writing.cs

#LineLine coverage
 1/*
 2 * Copyright 2017 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using CBAM.Abstractions;
 19using CBAM.SQL.Implementation;
 20using CBAM.SQL.PostgreSQL;
 21using CBAM.SQL.PostgreSQL.Implementation;
 22using System;
 23using System.Collections.Generic;
 24using System.IO;
 25using System.Linq;
 26using System.Text;
 27using System.Threading;
 28using System.Threading.Tasks;
 29using UtilPack;
 30using BackendExtraSizeInfo = System.ValueTuple<CBAM.SQL.PostgreSQL.BackendSizeInfo, System.Object>;
 31using MessageIOArgs = System.ValueTuple<CBAM.SQL.PostgreSQL.BackendABIHelper, System.IO.Stream, System.Threading.Cancell
 32using FormatCodeInfo = System.ValueTuple<CBAM.SQL.PostgreSQL.DataFormat[], System.Int32>;
 33
 34namespace CBAM.SQL.PostgreSQL.Implementation
 35{
 36   internal abstract class FrontEndMessage
 37   {
 38      private const Int32 MAX_MESSAGE_SIZE = 0x3fffffff;
 39
 40      public async Task SendMessageAsync(
 41         MessageIOArgs args,
 42         Boolean skipFlush = false
 43         )
 44      {
 45         var size = this.CalculateSize( args.Item1, args.Item4 ) + 4;
 46         if ( size > MAX_MESSAGE_SIZE )
 47         {
 48            // Backend's maxalloc size is largest message size that it can allocate
 49            throw new PgSQLException( "Too big message to send to backend (max is " + MAX_MESSAGE_SIZE + ", and " + this
 50         }
 51
 52         try
 53         {
 54            await this.DoSendMessageAsync( args.Item1, args.Item2, size, args.Item3, args.Item4 );
 55            if ( !skipFlush )
 56            {
 57               await args.Item2.FlushAsync( args.Item3 );
 58            }
 59         }
 60         catch ( Exception exc )
 61         {
 62            throw new PgSQLException( "Error when writing message to backend.", exc );
 63         }
 64      }
 65
 66      protected abstract Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array );
 67
 68      protected abstract Task DoSendMessageAsync( BackendABIHelper args, Stream stream, Int32 size, CancellationToken to
 69
 70
 71   }
 72   internal abstract class FrontEndMessageWithCode : FrontEndMessage
 73   {
 74      private const Int32 PREFIX_SIZE = sizeof( Byte ) + sizeof( Int32 );
 75
 76      private readonly FrontEndMessageCode _code;
 77
 78      internal FrontEndMessageWithCode( FrontEndMessageCode code )
 79      {
 80         this._code = code;
 81      }
 82
 83      protected override async Task DoSendMessageAsync(
 84         BackendABIHelper args,
 85         Stream stream,
 86         Int32 size,
 87         CancellationToken token,
 88         ResizableArray<Byte> array
 89         )
 90      {
 91         array.CurrentMaxCapacity = PREFIX_SIZE;
 92         var idx = 0;
 93
 94         array.Array
 95            .WriteByteToBytes( ref idx, (Byte) this._code )
 96            .WritePgInt32( ref idx, size );
 97         await stream.WriteAsync( array.Array, 0, PREFIX_SIZE, token );
 98         await this.PerformSendAfterWriteAsync( args, stream, size, token, array );
 99      }
 100
 101      protected abstract Task PerformSendAfterWriteAsync( BackendABIHelper args, Stream stream, Int32 size, Cancellation
 102
 103      internal FrontEndMessageCode Code
 104      {
 105         get
 106         {
 107            return this._code;
 108         }
 109      }
 110   }
 111
 112   internal sealed class FrontEndMessageWithNoContent : FrontEndMessageWithCode
 113   {
 114      internal static readonly FrontEndMessageWithNoContent TERMINATION = new FrontEndMessageWithNoContent( FrontEndMess
 115      internal static readonly FrontEndMessageWithNoContent SYNC = new FrontEndMessageWithNoContent( FrontEndMessageCode
 116      internal static readonly FrontEndMessageWithNoContent FLUSH = new FrontEndMessageWithNoContent( FrontEndMessageCod
 117      internal static readonly FrontEndMessageWithNoContent COPY_DONE = new FrontEndMessageWithNoContent( FrontEndMessag
 118
 119      private FrontEndMessageWithNoContent( FrontEndMessageCode code )
 120         : base( code )
 121      {
 122      }
 123
 124      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 125      {
 126         return 0;
 127      }
 128
 129      protected override Task PerformSendAfterWriteAsync( BackendABIHelper args, Stream stream, Int32 size, Cancellation
 130      {
 131         return TaskUtils.CompletedTask;
 132      }
 133   }
 134
 135   internal sealed class PasswordMessage : FrontEndMessageWithSingleBody
 136   {
 137      private readonly Byte[] _pw;
 138
 139      internal PasswordMessage( Byte[] pw )
 140         : base( FrontEndMessageCode.PasswordMessage )
 141      {
 142         ArgumentValidator.ValidateNotNull( "Password", pw );
 143
 144         this._pw = pw;
 145      }
 146
 147      protected override Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array )
 148      {
 149         return this._pw.Length;
 150      }
 151
 152      protected override void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array )
 153      {
 154         this._pw.CopyTo( array.Array, 0 );
 155      }
 156   }
 157
 158   internal sealed class StartupMessage : FrontEndMessage
 159   {
 160      private const Int32 PREFIX = 8;
 161
 162      private readonly Int32 _protocolVersion;
 163      private readonly IDictionary<String, String> _parameters;
 164
 165      internal StartupMessage(
 166         Int32 protocolVersion,
 167         IDictionary<String, String> parameters
 168         )
 169      {
 170         this._protocolVersion = protocolVersion;
 171         this._parameters = parameters ?? new Dictionary<String, String>();
 172      }
 173
 174      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 175      {
 176         return 5 // int32 (protocol version) + end-byte (after message, zero)
 177            + this._parameters.Sum( kvp => args.GetStringSize( kvp.Key, array ) + args.GetStringSize( kvp.Value, array )
 178      }
 179
 180      protected override async Task DoSendMessageAsync(
 181         BackendABIHelper args,
 182         Stream stream,
 183         Int32 size,
 184         CancellationToken token,
 185         ResizableArray<Byte> array
 186         )
 187      {
 188         // As documentation states, "For historical reasons, the very first message sent by the client (the startup mes
 189         // Hence we don't inherit the FrontEndMessageObjectWithCode class
 190         array.CurrentMaxCapacity = PREFIX;
 191         var idx = 0;
 192         array.Array
 193            .WritePgInt32( ref idx, size )
 194            .WritePgInt32( ref idx, this._protocolVersion );
 195         await stream.WriteAsync( array.Array, 0, PREFIX, token );
 196
 197         foreach ( var kvp in this._parameters )
 198         {
 199            await args.WriteString( stream, kvp.Key, token, array );
 200            await args.WriteString( stream, kvp.Value, token, array );
 201         }
 202         array.Array[0] = 0;
 203         await stream.WriteAsync( array.Array, 0, 1, token );
 204      }
 205   }
 206
 207   internal abstract class FrontEndMessageWithSingleBody : FrontEndMessageWithCode
 208   {
 209      public FrontEndMessageWithSingleBody( FrontEndMessageCode code )
 210         : base( code )
 211      {
 212      }
 213
 214      protected sealed override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 215      {
 216         return this.CalculateBufferSize( args, array );
 217      }
 218
 219      protected sealed override async Task PerformSendAfterWriteAsync(
 220         BackendABIHelper args,
 221         Stream stream,
 222         Int32 size,
 223         CancellationToken token,
 224         ResizableArray<Byte> array
 225         )
 226      {
 227         // Given size includes the integer that already has been written
 228         size -= sizeof( Int32 );
 229         array.CurrentMaxCapacity = size;
 230         this.WriteMessageToBuffer( args, array );
 231         await stream.WriteAsync( array.Array, 0, size, token );
 232      }
 233
 234      protected abstract Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array );
 235
 236      protected abstract void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array );
 237   }
 238
 239   internal sealed class QueryMessage : FrontEndMessageWithSingleBody
 240   {
 241      private readonly String _query;
 242
 243      internal QueryMessage( String query )
 244         : base( FrontEndMessageCode.Query )
 245      {
 246         this._query = query;
 247      }
 248
 249      protected override Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array )
 250      {
 251         return args.GetStringSize( this._query, array );
 252      }
 253
 254      protected override void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array )
 255      {
 256         var idx = 0;
 257         array.Array.WritePgString( ref idx, args.Encoding, this._query );
 258      }
 259   }
 260
 261   internal sealed class ParseMessage : FrontEndMessageWithCode
 262   {
 263      private readonly String _statementName;
 264      private readonly String _sql;
 265      private readonly Int32[] _typeIDs;
 266
 267      internal ParseMessage( String sql, Int32[] paramIndices, Int32[] typeIDs, String statementName = null )
 268         : base( FrontEndMessageCode.Parse )
 269      {
 270         ArgumentValidator.ValidateNotNull( "SQL", sql );
 271
 272         this._statementName = statementName;
 273         // Replace "blaa ? blaa2 ? blaa3" with "blaa $1 blaa2 $2 blaa3"
 274         var sb = new StringBuilder();
 275         var prev = 0;
 276         if ( paramIndices != null )
 277         {
 278            var curParam = 1;
 279            foreach ( var i in paramIndices )
 280            {
 281               sb.Append( sql.Substring( prev, i - prev ) )
 282               .Append( '$' ).Append( curParam++ );
 283               prev = i + 1;
 284            }
 285         }
 286
 287         sb.Append( sql.Substring( prev ) );
 288
 289         this._sql = sb.ToString();
 290         this._typeIDs = typeIDs;
 291      }
 292
 293      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 294      {
 295         return args.GetStringSize( this._statementName, array )
 296            + args.GetStringSize( this._sql, array )
 297            + sizeof( Int16 )
 298            + this._typeIDs.GetLengthOrDefault() * sizeof( Int32 );
 299      }
 300
 301      protected override async Task PerformSendAfterWriteAsync(
 302         BackendABIHelper args,
 303         Stream stream,
 304         Int32 size,
 305         CancellationToken token,
 306         ResizableArray<Byte> buffer
 307         )
 308      {
 309         await args.WriteString( stream, this._statementName, token, buffer );
 310         await args.WriteString( stream, this._sql, token, buffer );
 311
 312         var typesCount = this._typeIDs.GetLengthOrDefault();
 313         buffer.CurrentMaxCapacity = sizeof( Int16 ) + 4 * typesCount;
 314         var array = buffer.Array;
 315         var idx = 0;
 316         array.WritePgInt16( ref idx, typesCount );
 317         if ( typesCount > 0 )
 318         {
 319            foreach ( var typeID in this._typeIDs )
 320            {
 321               array.WritePgInt32( ref idx, typeID );
 322            }
 323         }
 324         await stream.WriteAsync( array, 0, idx, token );
 325      }
 326
 327      // Too many task allocations to make this feasible, at least at the moment (maybe when ValueTask is visible in .NE
 328      // Or make this use new WritePgString method for arrays...
 329      //private async Task SendSQL(
 330      //   MessageSendingArgs args,
 331      //   Stream stream
 332      //   )
 333      //{
 334      //   // Replace "blaa ? blaa2 ? blaa3" with "blaa $1 blaa2 $2 blaa3"
 335      //   var paramIndices = this._paramIndices;
 336      //   // Send first chunk
 337      //   var sql = this._sql;
 338      //   await args.WriteStringPart( stream, sql, 0, paramIndices.Length > 0 ? paramIndices[0] : sql.Length );
 339
 340      //   for (var i = 0; i < paramIndices.Length; ++i )
 341      //   {
 342      //      // Send $<number>
 343      //      var idx = 0;
 344      //      args.Buffer.Array[0] = 0x24; // '$'
 345      //      args.Buffer.Array.WritePgIntTextual( ref idx, i );
 346      //      await stream.WriteAsync( args.Buffer.Array, 0, idx );
 347      //      // Send next chunk
 348      //      var nextStartIndex = paramIndices[i] + 1;
 349      //      var nextEndIndex = i < paramIndices.Length - 1 ? paramIndices[i + 1] : sql.Length;
 350      //      await args.WriteStringPart( stream, sql, nextStartIndex, nextEndIndex - nextStartIndex );
 351      //   }
 352
 353      //   // Send terminating zero
 354      //   await args.WriteString( stream, null );
 355      // }
 356   }
 357
 358   internal sealed class CloseMessage : FrontEndMessageWithSingleBody
 359   {
 360      internal static readonly CloseMessage UNNAMED_STATEMENT = new CloseMessage( true );
 361      internal static readonly CloseMessage UNNAMED_PORTAL = new CloseMessage( false );
 362
 363      private readonly Boolean _isStatement;
 364      private readonly String _name;
 365
 366      internal CloseMessage( Boolean isStatement, String name = null )
 367         : base( FrontEndMessageCode.Close )
 368      {
 369         this._isStatement = isStatement;
 370         this._name = name;
 371      }
 372
 373      protected override Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array )
 374      {
 375         return 1 + args.GetStringSize( this._name, array );
 376      }
 377
 378      protected override void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array )
 379      {
 380         var idx = 0;
 381         array.Array
 382            .WriteByteToBytes( ref idx, (Byte) ( this._isStatement ? 'S' : 'P' ) )
 383            .WritePgString( ref idx, args.Encoding, this._name );
 384      }
 385   }
 386
 387
 388   internal sealed class BindMessage : FrontEndMessageWithCode
 389   {
 390      private readonly String _portalName;
 391      private readonly String _statementName;
 392      private readonly IEnumerable<StatementParameter> _params;
 393      private readonly TypeFunctionalityInformation[] _types;
 394      private readonly BackendExtraSizeInfo[] _preCreatedParamSizes;
 395      private readonly Boolean _disableBinarySend;
 396      private readonly Boolean _disableBinaryReceive;
 397      private readonly FormatCodeInfo _sendCodes;
 398      //private readonly FormatCodeInfo _receiveCodes;
 399
 400      internal BindMessage(
 401         IEnumerable<StatementParameter> paramz,
 402         Int32 paramCount,
 403         TypeFunctionalityInformation[] types,
 404         Boolean disableBinarySend,
 405         Boolean disableBinaryReceive,
 406         String portalName = null,
 407         String statementName = null
 408         )
 409         : base( FrontEndMessageCode.Bind )
 410      {
 411         this._portalName = portalName;
 412         this._statementName = statementName;
 413         this._disableBinarySend = disableBinarySend;
 414         this._disableBinaryReceive = disableBinaryReceive;
 415         this._params = paramz;
 416         this._types = types;
 417         this._preCreatedParamSizes = new BackendExtraSizeInfo[paramCount];
 418         this._sendCodes = this.GetFormatCodes( true );
 419         //this._receiveCodes = this.GetFormatCodes( false );
 420      }
 421
 422      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 423      {
 424         var retVal = args.GetStringSize( this._portalName, array )
 425            + args.GetStringSize( this._statementName, array )
 426            + 6 // paramFormatCount, paramCount, columnFormatCount
 427            + this._sendCodes.Item2 * sizeof( Int16 )
 428            //+ this._receiveCodes.Item2 * sizeof( Int16 )
 429            + this.CalculateParamSizes( args );
 430         return retVal;
 431      }
 432
 433      private Int32 CalculateParamSizes( BackendABIHelper args )
 434      {
 435         var paramCount = this._preCreatedParamSizes.Length;
 436         var size = 4 * paramCount; // Each parameter takes at least 4 bytes
 437         var i = 0;
 438         var formatCodes = this._sendCodes.Item1;
 439         foreach ( var param in this._params )
 440         {
 441            var val = param.ParameterValue;
 442            if ( val != null )
 443            {
 444               var thisType = this._types[i];
 445               // Change type if needed
 446               if ( !thisType.CLRType.Equals( val.GetType() ) )
 447               {
 448                  val = thisType.Functionality.ChangeTypeFrameworkToPgSQL( thisType.DatabaseData, val );
 449               }
 450
 451               var thisFormat = formatCodes == null ? DataFormat.Text : formatCodes[i >= formatCodes.Length ? 0 : i];
 452               var thisSizeInfo = thisType.Functionality.GetBackendSize( thisFormat, thisType.DatabaseData, args, val, f
 453
 454               this._preCreatedParamSizes[i] = (thisSizeInfo, val);
 455
 456
 457               size += thisSizeInfo.ByteCount;
 458            }
 459
 460            ++i;
 461         }
 462         return size;
 463      }
 464
 465      protected override async Task PerformSendAfterWriteAsync(
 466         BackendABIHelper args,
 467         Stream stream,
 468         Int32 size,
 469         CancellationToken token,
 470         ResizableArray<Byte> array
 471         )
 472      {
 473
 474         // Start building message
 475         await args.WriteString( stream, this._portalName, token, array );
 476         await args.WriteString( stream, this._statementName, token, array );
 477
 478         // Write format info about parameters
 479         var formatCodes = await this.WriteFormatInfo( args, stream, token, true, array );
 480
 481         // Write parameters
 482         var idx = 0;
 483         array.Array.WritePgInt16( ref idx, this._preCreatedParamSizes.Length );
 484         await stream.WriteAsync( array.Array, 0, idx, token );
 485         for ( var i = 0; i < this._preCreatedParamSizes.Length; ++i )
 486         {
 487            var thisSizeTuple = this._preCreatedParamSizes[i];
 488            var thisType = this._types[i];
 489            var thisStream = StreamFactory.CreateLimitedWriter(
 490               stream,
 491               thisSizeTuple.Item1.ByteCount + sizeof( Int32 ),
 492               token,
 493               array
 494               );
 495            try
 496            {
 497               await thisType.Functionality.WriteBackendValueCheckNull(
 498                  formatCodes == null ? DataFormat.Text : formatCodes[i >= formatCodes.Length ? 0 : i],
 499                  thisType.DatabaseData,
 500                  args,
 501                  thisStream,
 502                  thisSizeTuple.Item2,
 503                  thisSizeTuple.Item1,
 504                  false
 505                  );
 506
 507
 508            }
 509            finally
 510            {
 511               await thisStream.FlushAsync();
 512            }
 513         }
 514
 515         // Write format info for columns
 516         // Since we don't know the result column count (that'd require advanced SQL parsing), just set all formats to t
 517         // TODO add ResultColumnInfo: Type[] to StatementBuilder, which would be used for prepared statements, which wo
 518         idx = 0;
 519         array.Array.WritePgInt16( ref idx, 0 );
 520         await stream.WriteAsync( array.Array, 0, idx, token );
 521
 522         //await this.WriteFormatInfo( args, stream, token, false, array );
 523      }
 524
 525      private FormatCodeInfo GetFormatCodes( Boolean isForWriting )
 526      {
 527         //formatCodesNumber = 0;
 528         //return null;
 529         DataFormat[] formatCodes;
 530         Int32 formatCodesNumber;
 531         // Prepare format information
 532         if (
 533            ( isForWriting && this._disableBinarySend )
 534            || ( !isForWriting && this._disableBinaryReceive )
 535            || this._types.All( t => !( isForWriting ? t.Functionality.SupportsWritingBinaryFormat : t.Functionality.Sup
 536         {
 537            // All parameters use text format
 538            formatCodesNumber = 0;
 539            formatCodes = null;
 540         }
 541         else if ( this._types.All( t => isForWriting ? t.Functionality.SupportsWritingBinaryFormat : t.Functionality.Su
 542         {
 543            // All parameters use binary format
 544            formatCodesNumber = 1;
 545            formatCodes = new[] { DataFormat.Binary };
 546         }
 547         else
 548         {
 549            // Each parameter will use the most optimal format
 550            formatCodesNumber = this._types.Length;
 551            formatCodes = new DataFormat[formatCodesNumber];
 552            for ( var i = 0; i < formatCodes.Length; ++i )
 553            {
 554               var thisType = this._types[i];
 555               formatCodes[i] = ( isForWriting ? thisType.Functionality.SupportsWritingBinaryFormat : thisType.Functiona
 556            }
 557         }
 558         return (formatCodes, formatCodesNumber);
 559      }
 560
 561      private async Task<DataFormat[]> WriteFormatInfo(
 562         BackendABIHelper args,
 563         Stream stream,
 564         CancellationToken token,
 565         Boolean isForWriting,
 566         ResizableArray<Byte> buffer
 567         )
 568      {
 569         var formatCodesTuple = this._sendCodes; // isForWriting ? this._sendCodes : this._receiveCodes;
 570         var formatCodes = formatCodesTuple.Item1;
 571
 572         buffer.CurrentMaxCapacity = sizeof( Int16 ) + sizeof( Int16 ) * ( formatCodes?.Length ?? 0 );
 573         var idx = 0;
 574         var array = buffer.Array;
 575         array.WritePgInt16( ref idx, formatCodesTuple.Item2 );
 576
 577         if ( formatCodes != null )
 578         {
 579            // Format codes, if necessary
 580            for ( var i = 0; i < formatCodes.Length; ++i )
 581            {
 582               array.WritePgInt16( ref idx, (Int16) formatCodes[i] );
 583            }
 584         }
 585
 586         await stream.WriteAsync( array, 0, idx, token );
 587
 588         return formatCodes;
 589      }
 590   }
 591
 592   internal sealed class DescribeMessage : FrontEndMessageWithSingleBody
 593   {
 0594      internal static readonly DescribeMessage UNNAMED_STATEMENT = new DescribeMessage( true );
 0595      internal static readonly DescribeMessage UNNAMED_PORTAL = new DescribeMessage( false );
 596
 597      private readonly Boolean _isStatement;
 598      private readonly String _name;
 599
 600      internal DescribeMessage( Boolean isStatement, String name = null )
 45601         : base( FrontEndMessageCode.Describe )
 602      {
 48603         this._isStatement = isStatement;
 48604         this._name = name;
 47605      }
 606
 607      protected override Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array )
 608      {
 46609         return 1 + args.GetStringSize( this._name, array );
 610      }
 611
 612      protected override void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array )
 613      {
 49614         var idx = 0;
 49615         array.Array
 49616            .WriteByteToBytes( ref idx, (Byte) ( this._isStatement ? 'S' : 'P' ) )
 49617            .WritePgString( ref idx, args.Encoding, this._name );
 49618      }
 619   }
 620
 621   internal sealed class ExecuteMessage : FrontEndMessageWithSingleBody
 622   {
 623      internal static readonly ExecuteMessage UNNAMED_EXEC_ALL = new ExecuteMessage();
 624
 625      private readonly String _portalName;
 626      private readonly Int32 _maxRows;
 627
 628      internal ExecuteMessage( String portalName = null, Int32 maxRows = 0 )
 629         : base( FrontEndMessageCode.Execute )
 630      {
 631         this._portalName = portalName;
 632         this._maxRows = maxRows;
 633      }
 634
 635      protected override Int32 CalculateBufferSize( BackendABIHelper args, ResizableArray<Byte> array )
 636      {
 637         return args.GetStringSize( this._portalName, array ) + sizeof( Int32 );
 638      }
 639
 640      protected override void WriteMessageToBuffer( BackendABIHelper args, ResizableArray<Byte> array )
 641      {
 642         var idx = 0;
 643         array.Array
 644            .WritePgString( ref idx, args.Encoding, this._portalName )
 645            .WritePgInt32( ref idx, this._maxRows );
 646      }
 647   }
 648
 649   internal sealed class SSLRequestMessage : FrontEndMessage
 650   {
 651      internal static readonly SSLRequestMessage INSTANCE = new SSLRequestMessage();
 652
 653      private SSLRequestMessage()
 654      {
 655
 656      }
 657
 658      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 659      {
 660         return sizeof( Int32 );
 661      }
 662
 663      protected override async Task DoSendMessageAsync(
 664         BackendABIHelper args,
 665         Stream stream,
 666         Int32 size,
 667         CancellationToken token,
 668         ResizableArray<Byte> array
 669         )
 670      {
 671         var idx = 0;
 672         // We don't have front end message code, so we need to write size ourselves.
 673         array.Array
 674            .WritePgInt32( ref idx, size )
 675            .WritePgInt32( ref idx, 80877103 );// As per spec
 676         await stream.WriteAsync( array.Array, 0, idx, token );
 677      }
 678   }
 679
 680   internal sealed class CopyDataFrontEndMessage : FrontEndMessageWithCode
 681   {
 682      private readonly Byte[] _data;
 683      private readonly Int32 _offset;
 684      private readonly Int32 _count;
 685
 686      public CopyDataFrontEndMessage( Byte[] data, Int32 offset, Int32 count )
 687         : base( FrontEndMessageCode.CopyData )
 688      {
 689         this._data = data;
 690         this._offset = offset;
 691         this._count = count;
 692      }
 693
 694      protected override Int32 CalculateSize( BackendABIHelper args, ResizableArray<Byte> array )
 695      {
 696         return this._count - this._offset;
 697      }
 698
 699      protected override async Task PerformSendAfterWriteAsync(
 700         BackendABIHelper args,
 701         Stream stream,
 702         Int32 size,
 703         CancellationToken token,
 704         ResizableArray<Byte> array
 705         )
 706      {
 707         await stream.WriteAsync( this._data, this._offset, this._count, token );
 708      }
 709
 710   }
 711
 712   internal enum FrontEndMessageCode : byte
 713   {
 714      Bind = (Byte) 'B',
 715      Close = (Byte) 'C',
 716      Describe = (Byte) 'D',
 717      Execute = (Byte) 'E',
 718      //FunctionCall = (Byte) 'F', // Deprecated
 719      Flush = (Byte) 'H',
 720      Parse = (Byte) 'P',
 721      Query = (Byte) 'Q',
 722      Sync = (Byte) 'S',
 723      Termination = (Byte) 'X',
 724      PasswordMessage = (Byte) 'p',
 725      CopyDone = (Byte) 'c',
 726      CopyData = (Byte) 'd',
 727   }
 728
 729   internal static partial class CBAMExtensions
 730   {
 731      public static Byte[] WritePgInt16( this Byte[] array, ref Int32 idx, Int32 value )
 732      {
 733         array.WriteInt16BEToBytes( ref idx, (Int16) value );
 734         return array;
 735      }
 736
 737      public static Byte[] WritePgString( this Byte[] array, ref Int32 idx, IEncodingInfo encoding, String value )
 738      {
 739         if ( !String.IsNullOrEmpty( value ) )
 740         {
 741            idx += encoding.Encoding.GetBytes( value, 0, value.Length, array, idx );
 742         }
 743         array[idx++] = 0;
 744         return array;
 745      }
 746   }
 747}
 748
 749public static partial class E_CBAM
 750{
 751
 752
 753   internal static Int32 GetStringSize( this BackendABIHelper args, String str, ResizableArray<Byte> array )
 754   {
 755      var retVal = String.IsNullOrEmpty( str ) ? 1 : ( args.Encoding.Encoding.GetByteCount( str ) + 1 );
 756      array.CurrentMaxCapacity = retVal;
 757      return retVal;
 758   }
 759
 760   internal static async Task WriteString( this BackendABIHelper args, Stream stream, String str, CancellationToken toke
 761   {
 762      //if ( !String.IsNullOrEmpty( str ) )
 763      //{
 764      //   await args.WriteStringPart( stream, str, 0, str.Length );
 765      //}
 766      //args.Buffer.Array[0] = 0;
 767      //// Send terminating zero
 768      //await stream.WriteAsync( args.Buffer.Array, 0, 1 );
 769      var idx = 0;
 770      array.Array.WritePgString( ref idx, args.Encoding, str );
 771      await stream.WriteAsync( array.Array, 0, idx, token );
 772   }
 773
 774   //public static async Task WriteStringPart( this BackendABIHelper args, Stream stream, String str, Int32 stringStart,
 775   //{
 776   //   if ( !String.IsNullOrEmpty( str ) && stringLength > 0 )
 777   //   {
 778   //      var size = args.Encoding.Encoding.GetBytes( str, stringStart, stringLength, args.Buffer.Array, 0 );
 779   //      await stream.WriteAsync( args.Buffer.Array, 0, size );
 780   //   }
 781   //}
 782
 783
 784}