Summary

Class:CBAM.SQL.PostgreSQL.PgSQLTypeDatabaseData
Assembly:CBAM.SQL.PostgreSQL
File(s):/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL/TypeRegistry.cs
Covered lines:15
Uncovered lines:0
Coverable lines:15
Total lines:992
Line coverage:100%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.ctor(...)101%0%

File(s)

/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL/TypeRegistry.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.PostgreSQL;
 20using System;
 21using System.Collections.Generic;
 22using System.IO;
 23using System.Reflection;
 24using System.Text;
 25using System.Threading;
 26using System.Threading.Tasks;
 27using UtilPack;
 28
 29
 30namespace CBAM.SQL.PostgreSQL
 31{
 32
 33   using TSyncTextualSizeInfo = ValueTuple<Int32, String>;
 34
 35   /// <summary>
 36   /// This interface allows customization as to how <see cref="PgSQLConnection"/> handles SQL and CLR types and mapping
 37   /// </summary>
 38   /// <seealso cref="PgSQLConnection.TypeRegistry"/>
 39   /// <seealso cref="PgSQLTypeFunctionality"/>
 40   public interface TypeRegistry
 41   {
 42      /// <summary>
 43      /// Asynchronously adds functionality for given types so that the <see cref="PgSQLConnection"/> of this <see cref=
 44      /// </summary>
 45      /// <param name="typeFunctionalityInfos">The information about type functionalities, containing the type name in t
 46      /// <returns>Asynchronously returns the amount of functionalities actually processed. Any functionality informatio
 47      /// <exception cref="PgSQLException">If some error occurs during querying type IDs from database.</exception>
 48      ValueTask<Int32> AddTypeFunctionalitiesAsync( params (String DBTypeName, Type CLRType, Func<PgSQLTypeDatabaseData,
 49
 50      /// <summary>
 51      /// Tries to get <see cref="TypeFunctionalityInformation"/> for given type ID.
 52      /// </summary>
 53      /// <param name="typeID">The type ID, as stored in the database this connection is connected to.</param>
 54      /// <returns>A <see cref="TypeFunctionalityInformation"/> for given type ID, or <c>null</c> if type information fo
 55      TypeFunctionalityInformation TryGetTypeInfo( Int32 typeID );
 56
 57      /// <summary>
 58      /// Tries to get <see cref="TypeFunctionalityInformation"/> for given CLR type.
 59      /// </summary>
 60      /// <param name="clrType">The CLR <see cref="Type"/>.</param>
 61      /// <returns>A <see cref="TypeFunctionalityInformation"/> for given CLR type, or <c>null</c> if type information f
 62      /// <remarks>
 63      /// The <see cref="TypeFunctionalityInformation.CLRType"/> property of returned <see cref="TypeFunctionalityInform
 64      /// </remarks>
 65      TypeFunctionalityInformation TryGetTypeInfo( Type clrType );
 66
 67   }
 68
 69   //public struct TypeFunctionalityCreationParameters
 70   //{
 71   //   public TypeFunctionalityCreationParameters(
 72   //      TypeRegistry typeRegistry,
 73   //      PgSQLTypeDatabaseData databaseData
 74   //      )
 75   //   {
 76   //      this.TypeRegistry = ArgumentValidator.ValidateNotNull( nameof( typeRegistry ), typeRegistry );
 77   //      this.DatabaseData = ArgumentValidator.ValidateNotNull( nameof( databaseData ), databaseData );
 78   //   }
 79
 80   //   public TypeRegistry TypeRegistry { get; }
 81   //   public PgSQLTypeDatabaseData DatabaseData { get; }
 82   //}
 83
 84   /// <summary>
 85   /// This type is used as return type for callback which adds custom type functionality via <see cref="TypeRegistry.Ad
 86   /// </summary>
 87   public struct TypeFunctionalityCreationResult
 88   {
 89      /// <summary>
 90      /// Creates a new <see cref="TypeFunctionalityCreationResult"/> with given parameters.
 91      /// </summary>
 92      /// <param name="functionality">The <see cref="PgSQLTypeFunctionality"/> object.</param>
 93      /// <param name="isDefaultForCLRType">Whether the <paramref name="functionality"/> is the default for CLR type it 
 94      /// <exception cref="ArgumentNullException">If <paramref name="functionality"/> is <c>null</c>.</exception>
 95      public TypeFunctionalityCreationResult(
 96         PgSQLTypeFunctionality functionality,
 97         Boolean isDefaultForCLRType
 98         )
 99      {
 100         this.TypeFunctionality = ArgumentValidator.ValidateNotNull( nameof( functionality ), functionality );
 101         this.IsDefaultForCLRType = isDefaultForCLRType;
 102      }
 103
 104      /// <summary>
 105      /// Gets the <see cref="PgSQLTypeFunctionality"/> object.
 106      /// </summary>
 107      /// <value>The <see cref="PgSQLTypeFunctionality"/> object.</value>
 108      /// <seealso cref="PgSQLTypeFunctionality"/>
 109      public PgSQLTypeFunctionality TypeFunctionality { get; }
 110
 111      /// <summary>
 112      /// Gets the value indicating whether <see cref="TypeFunctionality"/> is the default for CLR type it represents.
 113      /// </summary>
 114      /// <value>The value indicating whether <see cref="TypeFunctionality"/> is the default for CLR type it represents.
 115      public Boolean IsDefaultForCLRType { get; }
 116   }
 117
 118   /// <summary>
 119   /// This class contains all information about a single mapping between PostgreSQL type and CLR type.
 120   /// </summary>
 121   public class TypeFunctionalityInformation
 122   {
 123      /// <summary>
 124      /// Creates a new instance of <see cref="TypeFunctionalityInformation"/> with given parameters.
 125      /// </summary>
 126      /// <param name="clrType">The CLR <see cref="Type"/> that <paramref name="functionality"/> supports.</param>
 127      /// <param name="functionality">The <see cref="PgSQLTypeFunctionality"/> for this type information.</param>
 128      /// <param name="databaseData">The <see cref="PgSQLTypeDatabaseData"/> containing type name and type ID for this t
 129      /// <remarks>The constructor is intended to be used mainly by <see cref="TypeRegistry"/> implementations.</remarks
 130      /// <exception cref="ArgumentNullException">If any of <paramref name="clrType"/>, <paramref name="functionality"/>
 131      public TypeFunctionalityInformation(
 132         Type clrType,
 133         PgSQLTypeFunctionality functionality,
 134         PgSQLTypeDatabaseData databaseData
 135         )
 136      {
 137         this.CLRType = ArgumentValidator.ValidateNotNull( nameof( clrType ), clrType );
 138         this.Functionality = ArgumentValidator.ValidateNotNull( nameof( functionality ), functionality );
 139         this.DatabaseData = ArgumentValidator.ValidateNotNull( nameof( databaseData ), databaseData );
 140      }
 141
 142      /// <summary>
 143      /// Gets the CLR <see cref="Type"/> of this type information.
 144      /// </summary>
 145      /// <value>The CLR <see cref="Type"/> of this type information.</value>
 146      public Type CLRType { get; }
 147
 148      /// <summary>
 149      /// Gets the <see cref="PgSQLTypeFunctionality"/> for this type information.
 150      /// </summary>
 151      /// <value>The <see cref="PgSQLTypeFunctionality"/> for this type information.</value>
 152      /// <seealso cref="PgSQLTypeFunctionality"/>
 153      public PgSQLTypeFunctionality Functionality { get; }
 154
 155      /// <summary>
 156      /// Gets the <see cref="PgSQLTypeDatabaseData"/> for this type information.
 157      /// This data contains the type name in the database, along with type ID (<c>oid</c>).
 158      /// </summary>
 159      /// <value>The <see cref="PgSQLTypeDatabaseData"/> for this type information.</value>
 160      /// <seealso cref="PgSQLTypeDatabaseData"/>
 161      public PgSQLTypeDatabaseData DatabaseData { get; }
 162   }
 163
 164   /// <summary>
 165   /// This interface contains all the API required by implementation of <see cref="PgSQLConnection"/> to serialize and 
 166   /// Objects implementing this interface are registered to <see cref="TypeRegistry"/> of the single <see cref="PgSQLCo
 167   /// </summary>
 168   /// <seealso cref="TypeRegistry"/>
 169   /// <seealso href="https://www.postgresql.org/docs/current/static/protocol.html"/>
 170   public interface PgSQLTypeFunctionality
 171   {
 172      /// <summary>
 173      /// Gets the value indicating whether this <see cref="PgSQLTypeFunctionality"/> supports reading the binary data f
 174      /// </summary>
 175      /// <value>The value indicating whether this <see cref="PgSQLTypeFunctionality"/> supports reading the binary data
 176      /// <seealso cref="DataFormat"/>
 177      Boolean SupportsReadingBinaryFormat { get; }
 178
 179      /// <summary>
 180      /// Gets the value indicating whether this <see cref="PgSQLTypeFunctionality"/> supports writing the binary data f
 181      /// </summary>
 182      /// <value>The value indicating whether this <see cref="PgSQLTypeFunctionality"/> supports writing the binary data
 183      /// <seealso cref="DataFormat"/>
 184      Boolean SupportsWritingBinaryFormat { get; }
 185
 186      /// <summary>
 187      /// Asynchronously performs deserializing of the value sent by backend into CLR object.
 188      /// </summary>
 189      /// <param name="dataFormat">The <see cref="DataFormat"/> the value is being sent by backend.</param>
 190      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 191      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 192      /// <param name="stream">The <see cref="StreamReaderWithResizableBufferAndLimitedSize"/> to use to read binary dat
 193      /// <returns>Asynchronously returns the CLR object deserialized from <paramref name="stream"/>.</returns>
 194      ValueTask<Object> ReadBackendValueAsync(
 195         DataFormat dataFormat,
 196         PgSQLTypeDatabaseData boundData,
 197         BackendABIHelper helper,
 198         StreamReaderWithResizableBufferAndLimitedSize stream
 199         );
 200
 201      /// <summary>
 202      /// Gets the size of value recognized by this <see cref="PgSQLTypeFunctionality"/>, in bytes.
 203      /// </summary>
 204      /// <param name="dataFormat">The <see cref="DataFormat"/> value is being sent to backend.</param>
 205      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 206      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 207      /// <param name="value">The value recognized by this <see cref="PgSQLTypeFunctionality"/>.</param>
 208      /// <param name="isArrayElement">Whether the <paramref name="value"/> is being sent inside SQL array.</param>
 209      /// <returns>The <see cref="BackendSizeInfo"/> object containing the byte count and optional custom information.</
 210      /// <seealso cref="DataFormat"/>
 211      /// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c>.</exception>
 212      BackendSizeInfo GetBackendSize(
 213         DataFormat dataFormat,
 214         PgSQLTypeDatabaseData boundData,
 215         BackendABIHelper helper,
 216         Object value,
 217         Boolean isArrayElement
 218         );
 219
 220      /// <summary>
 221      /// Asynchronously performs serializing of the CLR object into binary data sent to backend.
 222      /// </summary>
 223      /// <param name="dataFormat">The <see cref="DataFormat"/> of the data, as expected by backend.</param>
 224      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 225      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 226      /// <param name="stream">The <see cref="StreamWriterWithResizableBufferAndLimitedSize"/> to write binary data to.<
 227      /// <param name="value">The CLR object to serialize.</param>
 228      /// <param name="additionalInfoFromSize">The the <see cref="BackendSizeInfo"/>, as returned by <see cref="GetBacke
 229      /// <param name="isArrayElement">Whether <paramref name="value"/> is being sent inside SQL array.</param>
 230      /// <returns>Asychronously returns after the <paramref name="value"/> has been serialized.</returns>
 231      /// <seealso cref="DataFormat"/>
 232      /// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c>.</exception>
 233      Task WriteBackendValueAsync(
 234         DataFormat dataFormat,
 235         PgSQLTypeDatabaseData boundData,
 236         BackendABIHelper helper,
 237         StreamWriterWithResizableBufferAndLimitedSize stream,
 238         Object value,
 239         BackendSizeInfo additionalInfoFromSize,
 240         Boolean isArrayElement
 241         );
 242
 243      /// <summary>
 244      /// Tries to change some object to the type recognized by this <see cref="PgSQLTypeFunctionality"/>.
 245      /// </summary>
 246      /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 247      /// <param name="obj">The object to change type to type recognized by this <see cref="PgSQLTypeFunctionality"/>.</
 248      /// <returns>The object of type recognized by this <see cref="PgSQLTypeFunctionality"/>.</returns>
 249      /// <exception cref="ArgumentNullException">If <paramref name="obj"/> is <c>null</c>.</exception>
 250      /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to chang
 251      Object ChangeTypeFrameworkToPgSQL( PgSQLTypeDatabaseData dbData, Object obj );
 252
 253      /// <summary>
 254      /// Changes the object deserialized by <see cref="ReadBackendValueAsync"/> method to another type.
 255      /// </summary>
 256      /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 257      /// <param name="obj">The object to change type.</param>
 258      /// <param name="typeTo">The type to change <paramref name="obj"/> to.</param>
 259      /// <returns>The object of given type.</returns>
 260      /// <exception cref="ArgumentNullException">If <paramref name="obj"/> is <c>null</c>.</exception>
 261      /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to chang
 262      Object ChangeTypePgSQLToFramework( PgSQLTypeDatabaseData dbData, Object obj, Type typeTo );
 263   }
 264
 265   /// <summary>
 266   /// This structure contains information about the size of some object when it is being sent to PostgreSQL backend.
 267   /// The method <see cref="PgSQLTypeFunctionality.GetBackendSize"/> uses this structure as return type.
 268   /// </summary>
 269   public struct BackendSizeInfo
 270   {
 271      /// <summary>
 272      /// Creates a new instance of <see cref="BackendSizeInfo"/> with given parameters.
 273      /// </summary>
 274      /// <param name="byteCount">The amount of bytes that the object being serialized will take.</param>
 275      /// <param name="customInformation">Optional custom information to pass to <see cref="PgSQLTypeFunctionality.Write
 276      public BackendSizeInfo(
 277         Int32 byteCount,
 278         Object customInformation = null
 279         )
 280      {
 281         this.ByteCount = byteCount;
 282         this.CustomInformation = customInformation;
 283      }
 284
 285      /// <summary>
 286      /// Gets the amount of bytes that the object being serialized will take.
 287      /// </summary>
 288      /// <value>The amount of bytes that the object being serialized will take.</value>
 289      public Int32 ByteCount { get; }
 290
 291      /// <summary>
 292      /// Gets optional custom information to pass to <see cref="PgSQLTypeFunctionality.WriteBackendValueAsync"/> method
 293      /// </summary>
 294      /// <value>Optional custom information to pass to <see cref="PgSQLTypeFunctionality.WriteBackendValueAsync"/> meth
 295      public Object CustomInformation { get; }
 296   }
 297
 298   /// <summary>
 299   /// This class contains useful utilities and methods used by <see cref="PgSQLTypeFunctionality"/> objects when they s
 300   /// </summary>
 301   public class BackendABIHelper
 302   {
 303      private readonly BinaryStringPool _stringPool;
 304
 305      /// <summary>
 306      /// Creates a new instance of <see cref="BackendABIHelper"/> with given parameters.
 307      /// </summary>
 308      /// <param name="encoding">The <see cref="IEncodingInfo"/> to use.</param>
 309      /// <param name="stringPool">The <see cref="BinaryStringPool"/> to use.</param>
 310      /// <exception cref="ArgumentNullException">If <paramref name="encoding"/> or <paramref name="stringPool"/> is <c>
 311      public BackendABIHelper(
 312         IEncodingInfo encoding,
 313         BinaryStringPool stringPool
 314         )
 315      {
 316         this._stringPool = ArgumentValidator.ValidateNotNull( nameof( stringPool ), stringPool );
 317         this.CharacterReader = new StreamCharacterReaderLogic( encoding );
 318         this.CharacterWriter = new StreamCharacterWriterLogic( encoding, 1024 );
 319      }
 320
 321      /// <summary>
 322      /// Gets the <see cref="IEncodingInfo"/> of this connection.
 323      /// </summary>
 324      /// <value>The <see cref="IEncodingInfo"/> of this connection.</value>
 325      public IEncodingInfo Encoding
 326      {
 327         get
 328         {
 329            return this.CharacterReader.Encoding;
 330         }
 331      }
 332
 333      /// <summary>
 334      /// Gets the <see cref="StreamCharacterReaderLogic"/> of this connection.
 335      /// </summary>
 336      /// <value>The <see cref="StreamCharacterReaderLogic"/> of this connection.</value>
 337      public StreamCharacterReaderLogic CharacterReader { get; }
 338
 339      /// <summary>
 340      /// Gets the <see cref="StreamCharacterWriterLogic"/> of this connection.
 341      /// </summary>
 342      /// <value>The <see cref="StreamCharacterWriterLogic"/> of this connection.</value>
 343      public StreamCharacterWriterLogic CharacterWriter { get; }
 344
 345      /// <summary>
 346      /// Gets pooled string or deserializes from given binary data and pools the string.
 347      /// </summary>
 348      /// <param name="array">The binary data.</param>
 349      /// <param name="offset">The offset in <paramref name="array"/> where to start reading data.</param>
 350      /// <param name="count">The amount of bytes to read in <paramref name="array"/>.</param>
 351      /// <returns>Pooled or deserialized string.</returns>
 352      public String GetStringWithPool( Byte[] array, Int32 offset, Int32 count )
 353      {
 354         return this._stringPool.GetString( array, offset, count );
 355      }
 356
 357   }
 358
 359   /// <summary>
 360   /// This enumeration describes the data format used when (de)serializing CLR objects from and to the backend.
 361   /// </summary>
 362   /// <seealso href="https://www.postgresql.org/docs/current/static/protocol-overview.html#PROTOCOL-FORMAT-CODES"/>
 363   public enum DataFormat : short
 364   {
 365      /// <summary>
 366      /// This value signifies that the binary data is in text format.
 367      /// </summary>
 368      Text = 0,
 369
 370      /// <summary>
 371      /// This value signifies that the binary data is in binary format.
 372      /// </summary>
 373      Binary = 1,
 374   }
 375
 376   /// <summary>
 377   /// This is utility class containing some useful and common information when (de)serializing CLR objects from and to 
 378   /// </summary>
 379   public abstract class CommonPgSQLTypeFunctionalityInfo
 380   {
 381      static CommonPgSQLTypeFunctionalityInfo()
 382      {
 383         var format = (System.Globalization.NumberFormatInfo) System.Globalization.CultureInfo.InvariantCulture.NumberFo
 384         format.NumberDecimalDigits = 15;
 385         NumberFormat = format;
 386      }
 387
 388      /// <summary>
 389      /// Gets the <see cref="System.Globalization.NumberFormatInfo"/> to use when (de)serializing numerical values.
 390      /// </summary>
 391      /// <value>The <see cref="System.Globalization.NumberFormatInfo"/> to use when (de)serializing numerical values.</
 392      public static System.Globalization.NumberFormatInfo NumberFormat { get; }
 393
 394   }
 395
 396   /// <summary>
 397   /// This class implements <see cref="PgSQLTypeFunctionality"/> by redirecting all methods to callbacks given to const
 398   /// </summary>
 399   /// <typeparam name="TValue">The type supported by this <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.</typepar
 400   /// <remarks>
 401   /// Usually, the <see cref="CreateSingleBodyUnboundInfo"/> method is used to create instances of this class.
 402   /// </remarks>
 403   public class DefaultPgSQLTypeFunctionality<TValue> : PgSQLTypeFunctionality
 404   {
 405
 406
 407      private readonly ReadFromBackend<TValue> _text2CLR;
 408      private readonly ReadFromBackend<TValue> _binary2CLR;
 409      private readonly ChangePgSQLToSystem<TValue> _pg2System;
 410      private readonly ChangeSystemToPgSQL<TValue> _system2PG;
 411      private readonly CalculateBackendSize<TValue, BackendSizeInfo> _clr2BinarySize;
 412      private readonly WriteToBackend<TValue> _clr2Binary;
 413      private readonly CalculateBackendSize<TValue, BackendSizeInfo> _clr2TextSize;
 414      private readonly WriteToBackend<TValue> _clr2Text;
 415
 416      /// <summary>
 417      /// Creates a new instance of <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> with given callbacks.
 418      /// Note that usually <see cref="CreateSingleBodyUnboundInfo"/> method is used to create new instance, but in case
 419      /// </summary>
 420      /// <param name="text2CLR">The callback used by <see cref="ReadBackendValueAsync"/> method when the <see cref="Dat
 421      /// <param name="binary2CLR">The callback used by <see cref="ReadBackendValueAsync"/> method when the <see cref="D
 422      /// <param name="clr2TextSize">The callback used by <see cref="GetBackendSize"/> method when the <see cref="DataFo
 423      /// <param name="clr2BinarySize">The callback used by <see cref="GetBackendSize"/> method when the <see cref="Data
 424      /// <param name="clr2Text">The callback used by <see cref="WriteBackendValueAsync"/> method when the <see cref="Da
 425      /// <param name="clr2Binary">The callback used by <see cref="WriteBackendValueAsync"/> method when the <see cref="
 426      /// <param name="pgSQL2System">The callack used by <see cref="ChangeTypePgSQLToFramework"/> method. If <c>null</c>
 427      /// <param name="system2PgSQL">The callback used by <see cref="ChangeTypeFrameworkToPgSQL"/> method. If <c>null</c
 428      public DefaultPgSQLTypeFunctionality(
 429         ReadFromBackend<TValue> text2CLR,
 430         ReadFromBackend<TValue> binary2CLR,
 431         CalculateBackendSize<TValue, BackendSizeInfo> clr2TextSize,
 432         CalculateBackendSize<TValue, BackendSizeInfo> clr2BinarySize,
 433         WriteToBackend<TValue> clr2Text,
 434         WriteToBackend<TValue> clr2Binary,
 435         ChangePgSQLToSystem<TValue> pgSQL2System,
 436         ChangeSystemToPgSQL<TValue> system2PgSQL
 437         )
 438      {
 439
 440         this._text2CLR = text2CLR;
 441         this._binary2CLR = binary2CLR;
 442         this._pg2System = pgSQL2System;
 443         this._system2PG = system2PgSQL;
 444         this._clr2BinarySize = clr2BinarySize;
 445         this._clr2Binary = clr2Binary;
 446         this._clr2TextSize = clr2TextSize;
 447         this._clr2Text = clr2Text;
 448      }
 449
 450      /// <summary>
 451      /// Implements <see cref="PgSQLTypeFunctionality.SupportsReadingBinaryFormat"/> by checking whether the appropriat
 452      /// </summary>
 453      /// <value>Value indicating whether appropriate <see cref="ReadFromBackend{TValue}"/> callback was given to constr
 454      public Boolean SupportsReadingBinaryFormat
 455      {
 456         get
 457         {
 458            return this._binary2CLR != null;
 459         }
 460      }
 461
 462      /// <summary>
 463      /// Implements <see cref="PgSQLTypeFunctionality.SupportsWritingBinaryFormat"/> by checking whether the appropriat
 464      /// </summary>
 465      /// <value>Value indicating whether appropriate <see cref="CalculateBackendSize{TValue, TResult}"/> and <see cref=
 466      public Boolean SupportsWritingBinaryFormat
 467      {
 468         get
 469         {
 470            return this._clr2BinarySize != null && this._clr2Binary != null;
 471         }
 472      }
 473
 474      /// <summary>
 475      /// Implements <see cref="PgSQLTypeFunctionality.ChangeTypePgSQLToFramework"/> by either calling the <see cref="Ch
 476      /// </summary>
 477      /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 478      /// <param name="obj">The object to change type.</param>
 479      /// <param name="typeTo">The type to change <paramref name="obj"/> to.</param>
 480      /// <returns>The object of given type.</returns>
 481      /// <exception cref="ArgumentNullException">If <paramref name="obj"/> is <c>null</c>.</exception>
 482      /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to chang
 483      public Object ChangeTypePgSQLToFramework( PgSQLTypeDatabaseData dbData, Object obj, Type typeTo )
 484      {
 485         ArgumentValidator.ValidateNotNull( nameof( obj ), obj );
 486
 487         return this._pg2System == null ?
 488            Convert.ChangeType( obj, typeTo, System.Globalization.CultureInfo.InvariantCulture ) :
 489            this._pg2System( dbData, (TValue) obj, typeTo );
 490      }
 491
 492      /// <summary>
 493      /// Implements <see cref="PgSQLTypeFunctionality.ChangeTypeFrameworkToPgSQL"/> by either calling the <see cref="Ch
 494      /// </summary>
 495      /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 496      /// <param name="obj">The object to change type to type recognized by this <see cref="PgSQLTypeFunctionality"/>.</
 497      /// <returns>The object of type recognized by this <see cref="PgSQLTypeFunctionality"/>.</returns>
 498      /// <exception cref="ArgumentNullException">If <paramref name="obj"/> is <c>null</c>.</exception>
 499      /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to chang
 500      public Object ChangeTypeFrameworkToPgSQL( PgSQLTypeDatabaseData dbData, Object obj )
 501      {
 502         ArgumentValidator.ValidateNotNull( nameof( obj ), obj );
 503
 504         return this._system2PG == null ?
 505            Convert.ChangeType( obj, typeof( TValue ), System.Globalization.CultureInfo.InvariantCulture ) :
 506            this._system2PG( dbData, obj );
 507      }
 508
 509      /// <summary>
 510      /// Implements <see cref="PgSQLTypeFunctionality.GetBackendSize"/> by calling appropriate <see cref="CalculateBack
 511      /// </summary>
 512      /// <param name="dataFormat">The <see cref="DataFormat"/> value is being sent to backend.</param>
 513      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 514      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 515      /// <param name="value">The value recognized by this <see cref="PgSQLTypeFunctionality"/>.</param>
 516      /// <param name="isArrayElement">Whether the <paramref name="value"/> is being sent inside SQL array.</param>
 517      /// <returns>The <see cref="BackendSizeInfo"/> object containing the byte count and optional custom information.</
 518      /// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c>.</exception>
 519      /// <exception cref="InvalidCastException">If <paramref name="value"/> is not of type <typeparamref name="TValue"/
 520      /// <exception cref="NotSupportedException">If given <paramref name="dataFormat"/> is not supported - either becau
 521      public BackendSizeInfo GetBackendSize( DataFormat dataFormat, PgSQLTypeDatabaseData boundData, BackendABIHelper he
 522      {
 523         ArgumentValidator.ValidateNotNull( nameof( value ), value );
 524         switch ( dataFormat )
 525         {
 526            case DataFormat.Text:
 527               return CheckDelegate( this._clr2TextSize, dataFormat )( boundData, helper.Encoding, (TValue) value, isArr
 528            case DataFormat.Binary:
 529               return CheckDelegate( this._clr2BinarySize, dataFormat )( boundData, helper.Encoding, (TValue) value, isA
 530            default:
 531               throw new NotSupportedException( $"Data format {dataFormat} is not recognized." );
 532         }
 533      }
 534
 535      /// <summary>
 536      /// Implements <see cref="PgSQLTypeFunctionality.WriteBackendValueAsync"/> by calling appropriate <see cref="Write
 537      /// </summary>
 538      /// <param name="dataFormat">The <see cref="DataFormat"/> of the data, as expected by backend.</param>
 539      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 540      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 541      /// <param name="stream">The <see cref="StreamWriterWithResizableBufferAndLimitedSize"/> to write binary data to.<
 542      /// <param name="value">The CLR object to serialize.</param>
 543      /// <param name="additionalInfoFromSize">The the <see cref="BackendSizeInfo"/>, as returned by <see cref="GetBacke
 544      /// <param name="isArrayElement">Whether <paramref name="value"/> is being sent inside SQL array.</param>
 545      /// <returns>Asychronously returns after the <paramref name="value"/> has been serialized.</returns>
 546      /// <exception cref="ArgumentNullException">If <paramref name="value"/> is <c>null</c>.</exception>
 547      /// <exception cref="InvalidCastException">If <paramref name="value"/> is not of type <typeparamref name="TValue"/
 548      /// <exception cref="NotSupportedException">If given <paramref name="dataFormat"/> is not supported - either becau
 549      public Task WriteBackendValueAsync( DataFormat dataFormat, PgSQLTypeDatabaseData boundData, BackendABIHelper helpe
 550      {
 551         ArgumentValidator.ValidateNotNull( nameof( value ), value );
 552         switch ( dataFormat )
 553         {
 554            case DataFormat.Text:
 555               return CheckDelegate( this._clr2Text, dataFormat )( boundData, helper, stream, (TValue) value, additional
 556            case DataFormat.Binary:
 557               return CheckDelegate( this._clr2Binary, dataFormat )( boundData, helper, stream, (TValue) value, addition
 558            default:
 559               throw new NotSupportedException( $"Data format {dataFormat} is not recognized." );
 560         }
 561      }
 562
 563      /// <summary>
 564      /// Implements <see cref="PgSQLTypeFunctionality.ReadBackendValueAsync"/> by calling appropriate <see cref="ReadFr
 565      /// </summary>
 566      /// <param name="dataFormat">The <see cref="DataFormat"/> the value is being sent by backend.</param>
 567      /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specif
 568      /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 569      /// <param name="stream">The <see cref="StreamReaderWithResizableBufferAndLimitedSize"/> to use to read binary dat
 570      /// <returns>Asynchronously returns the CLR object deserialized from <paramref name="stream"/>.</returns>
 571      /// <exception cref="NotSupportedException">If given <paramref name="dataFormat"/> is not supported - either becau
 572      public async ValueTask<Object> ReadBackendValueAsync(
 573         DataFormat dataFormat,
 574         PgSQLTypeDatabaseData boundData,
 575         BackendABIHelper helper,
 576         StreamReaderWithResizableBufferAndLimitedSize stream
 577         )
 578      {
 579         switch ( dataFormat )
 580         {
 581            case DataFormat.Binary:
 582               return await CheckDelegate( this._binary2CLR, dataFormat )( boundData, helper, stream );
 583            case DataFormat.Text:
 584               return await CheckDelegate( this._text2CLR, dataFormat )( boundData, helper, stream );
 585            default:
 586               throw new NotSupportedException( $"Data format {dataFormat} is not recognized." );
 587         }
 588      }
 589
 590      private static T CheckDelegate<T>( T del, DataFormat dataFormat )
 591         where T : class
 592      {
 593         if ( del == null )
 594         {
 595            throw new NotSupportedException( $"The data format {dataFormat} is not supported." );
 596         }
 597         return del;
 598      }
 599
 600      /// <summary>
 601      /// Creates a new instance of <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> which will read and write whole 
 602      /// </summary>
 603      /// <param name="text2CLR">Synchronous <see cref="ReadFromBackendSync{TValue}"/> callback to deserialize textual d
 604      /// <param name="binary2CLR">Synchronous <see cref="ReadFromBackendSync{TValue}"/> callback to deserialize binary 
 605      /// <param name="clr2TextSize">The <see cref="CalculateBackendSize{TValue, TResult}"/> callback to calculate byte 
 606      /// <param name="clr2BinarySize">The <see cref="CalculateBackendSize{TValue, TResult}"/> callback to calculate byt
 607      /// <param name="clr2Text">Synchronous <see cref="WriteToBackendSync{TValue, TResult}"/> callback to serialize CLR
 608      /// <param name="clr2Binary">Synchronous <see cref="WriteToBackendSync{TValue, TSizeInfo}"/> callback to serialize
 609      /// <param name="pgSQL2System">Callback to convert object of type <typeparamref name="TValue"/> into given type.</
 610      /// <param name="system2PgSQL">Callback to convert object into <typeparamref name="TValue"/>.</param>
 611      /// <returns>A new instance of <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> which uses given callbacks to i
 612      /// <exception cref="ArgumentNullException">If <paramref name="text2CLR"/> is <c>null</c></exception>
 613      public static DefaultPgSQLTypeFunctionality<TValue> CreateSingleBodyUnboundInfo(
 614         ReadFromBackendSync<TValue> text2CLR,
 615         ReadFromBackendSync<TValue> binary2CLR,
 616         CalculateBackendSize<TValue, EitherOr<Int32, String>> clr2TextSize,
 617         CalculateBackendSize<TValue, Int32> clr2BinarySize,
 618         WriteToBackendSync<TValue, TSyncTextualSizeInfo> clr2Text,
 619         WriteToBackendSync<TValue, Int32> clr2Binary,
 620         ChangePgSQLToSystem<TValue> pgSQL2System,
 621         ChangeSystemToPgSQL<TValue> system2PgSQL
 622         )
 623      {
 624         ArgumentValidator.ValidateNotNull( nameof( text2CLR ), text2CLR );
 625
 626         CalculateBackendSize<TValue, BackendSizeInfo> textSizeActual;
 627         if ( clr2TextSize == null )
 628         {
 629            if ( typeof( IFormattable ).GetTypeInfo().IsAssignableFrom( typeof( TValue ).GetTypeInfo() ) )
 630            {
 631               textSizeActual = ( PgSQLTypeDatabaseData boundData, IEncodingInfo encoding, TValue value, Boolean isArray
 632               {
 633                  var str = ( (IFormattable) value ).ToString( null, CommonPgSQLTypeFunctionalityInfo.NumberFormat );
 634                  return new BackendSizeInfo( encoding.Encoding.GetByteCount( str ), str );
 635               };
 636            }
 637            else
 638            {
 639               textSizeActual = ( PgSQLTypeDatabaseData boundData, IEncodingInfo encoding, TValue value, Boolean isArray
 640               {
 641                  var str = value.ToString();
 642                  return new BackendSizeInfo( encoding.Encoding.GetByteCount( str ), str );
 643               };
 644            }
 645         }
 646         else
 647         {
 648            textSizeActual = ( PgSQLTypeDatabaseData boundData, IEncodingInfo encoding, TValue value, Boolean isArrayEle
 649            {
 650               var thisTextSize = clr2TextSize( boundData, encoding, value, isArrayElement );
 651               return thisTextSize.IsFirst ? new BackendSizeInfo( thisTextSize.First ) : new BackendSizeInfo( encoding.E
 652            };
 653         }
 654
 655         WriteToBackendSync<TValue, TSyncTextualSizeInfo> clr2TextActual;
 656         if ( clr2Text == null )
 657         {
 658            clr2TextActual = ( PgSQLTypeDatabaseData boundData, BackendABIHelper args, Byte[] array, Int32 offset, TValu
 659            {
 660               var str = additionalInfoFromSize.Item2;
 661               args.Encoding.Encoding.GetBytes( str, 0, str.Length, array, offset );
 662            };
 663         }
 664         else
 665         {
 666            clr2TextActual = clr2Text;
 667         }
 668
 669         return new DefaultPgSQLTypeFunctionality<TValue>(
 670            async ( PgSQLTypeDatabaseData boundData, BackendABIHelper args, StreamReaderWithResizableBufferAndLimitedSiz
 671            {
 672               if ( stream != null )
 673               {
 674                  await stream.ReadAllBytesToBuffer();
 675               }
 676               return text2CLR( boundData, args, stream.Buffer, 0, (Int32) stream.TotalByteCount );
 677            },
 678            binary2CLR == null ? (ReadFromBackend<TValue>) null : async ( PgSQLTypeDatabaseData boundData, BackendABIHel
 679             {
 680                if ( stream != null )
 681                {
 682                   await stream.ReadAllBytesToBuffer();
 683                }
 684
 685                return binary2CLR( boundData, args, stream.Buffer, 0, (Int32) stream.TotalByteCount );
 686             },
 687            textSizeActual,
 688            clr2BinarySize == null ? (CalculateBackendSize<TValue, BackendSizeInfo>) null : ( PgSQLTypeDatabaseData boun
 689            async ( PgSQLTypeDatabaseData boundData, BackendABIHelper args, StreamWriterWithResizableBufferAndLimitedSiz
 690            {
 691               (var offset, var count) = stream.ReserveBufferSegment( additionalInfoFromSize.ByteCount );
 692               clr2TextActual( boundData, args, stream.Buffer, offset, value, (additionalInfoFromSize.ByteCount, (String
 693               await stream.FlushAsync();
 694            },
 695            clr2Binary == null ? (WriteToBackend<TValue>) null : async ( PgSQLTypeDatabaseData boundData, BackendABIHelp
 696            {
 697               (var offset, var count) = stream.ReserveBufferSegment( additionalInfoFromSize.ByteCount );
 698               clr2Binary( boundData, args, stream.Buffer, offset, value, additionalInfoFromSize.ByteCount, isArrayEleme
 699               await stream.FlushAsync();
 700            },
 701            pgSQL2System,
 702            system2PgSQL
 703            );
 704      }
 705   }
 706
 707   /// <summary>
 708   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> in its <see cref="DefaultPgSQLTypeFu
 709   /// </summary>
 710   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 711   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 712   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 713   /// <param name="stream">The <see cref="StreamReaderWithResizableBufferAndLimitedSize"/> to use to read binary data f
 714   /// <returns>Potentially asynchronously returns deserialized value from <paramref name="stream"/>.</returns>
 715   /// <remarks>
 716   /// The <see cref="DataFormat"/> is assumed to be known by this callback.
 717   /// </remarks>
 718   public delegate ValueTask<TValue> ReadFromBackend<TValue>( PgSQLTypeDatabaseData dbData, BackendABIHelper helper, Str
 719
 720   /// <summary>
 721   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> in its <see cref="DefaultPgSQLTypeFu
 722   /// </summary>
 723   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 724   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 725   /// <param name="pgSQLObject">The object to change type.</param>
 726   /// <param name="targetType">The type to change <paramref name="pgSQLObject"/> to.</param>
 727   /// <returns>The object of given type.</returns>
 728   /// <exception cref="ArgumentNullException">If <paramref name="pgSQLObject"/> is <c>null</c>.</exception>
 729   /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to change t
 730   public delegate Object ChangePgSQLToSystem<TValue>( PgSQLTypeDatabaseData dbData, TValue pgSQLObject, Type targetType
 731
 732   /// <summary>
 733   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> in its <see cref="DefaultPgSQLTypeFu
 734   /// </summary>
 735   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 736   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 737   /// <param name="systemObject">The object to change type to type recognized by this <see cref="PgSQLTypeFunctionality
 738   /// <returns>The object of type recognized by this <see cref="PgSQLTypeFunctionality"/>.</returns>
 739   /// <exception cref="ArgumentNullException">If <paramref name="systemObject"/> is <c>null</c>.</exception>
 740   /// <exception cref="InvalidCastException">If this <see cref="PgSQLTypeFunctionality"/> does not know how to change t
 741   public delegate TValue ChangeSystemToPgSQL<TValue>( PgSQLTypeDatabaseData dbData, Object systemObject );
 742
 743   /// <summary>
 744   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> in its <see cref="DefaultPgSQLTypeFu
 745   /// </summary>
 746   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 747   /// <typeparam name="TResult">The type of calculation result. The <see cref="DefaultPgSQLTypeFunctionality{TValue}.Ge
 748   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 749   /// <param name="encoding">The <see cref="IEncodingInfo"/> used for text (de)serialization.</param>
 750   /// <param name="value">The value recognized by this <see cref="PgSQLTypeFunctionality"/>.</param>
 751   /// <param name="isArrayElement">Whether the <paramref name="value"/> is being sent inside SQL array.</param>
 752   /// <returns>The result of calculation.</returns>
 753   public delegate TResult CalculateBackendSize<TValue, TResult>( PgSQLTypeDatabaseData dbData, IEncodingInfo encoding, 
 754
 755   /// <summary>
 756   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/> in its <see cref="DefaultPgSQLTypeFu
 757   /// </summary>
 758   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 759   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 760   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 761   /// <param name="stream">The <see cref="StreamWriterWithResizableBufferAndLimitedSize"/> to write binary data to.</pa
 762   /// <param name="value">The CLR object to serialize.</param>
 763   /// <param name="additionalInfoFromSize">The the <see cref="BackendSizeInfo"/>, as returned by <see cref="CalculateBa
 764   /// <param name="isArrayElement">Whether <paramref name="value"/> is being sent inside SQL array.</param>
 765   /// <returns>Task which will complete once value has been written to <paramref name="stream"/>.</returns>
 766   public delegate Task WriteToBackend<TValue>( PgSQLTypeDatabaseData dbData, BackendABIHelper helper, StreamWriterWithR
 767
 768   /// <summary>
 769   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}.CreateSingleBodyUnboundInfo"/>, to sync
 770   /// </summary>
 771   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 772   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 773   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 774   /// <param name="array">The byte array containing whole data.</param>
 775   /// <param name="offset">The offset in <paramref name="array"/> where to start reading.</param>
 776   /// <param name="count">The amount of bytes to read from <paramref name="array"/>.</param>
 777   /// <returns>The deserialized value.</returns>
 778   public delegate TValue ReadFromBackendSync<TValue>( PgSQLTypeDatabaseData dbData, BackendABIHelper helper, Byte[] arr
 779
 780   /// <summary>
 781   /// This callback is used by <see cref="DefaultPgSQLTypeFunctionality{TValue}.CreateSingleBodyUnboundInfo"/>, to sync
 782   /// </summary>
 783   /// <typeparam name="TValue">The type of the value understood by <see cref="DefaultPgSQLTypeFunctionality{TValue}"/>.
 784   /// <typeparam name="TSizeInfo">The type of size information (return type of <see cref="CalculateBackendSize{TValue, 
 785   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 786   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 787   /// <param name="array">The byte array to write data to.</param>
 788   /// <param name="offset">The offset in <paramref name="array"/> where to start writing.</param>
 789   /// <param name="value">The vlue to serialize.</param>
 790   /// <param name="additionalInfoFromSize">The result of calling the corresponding <see cref="CalculateBackendSize{TVal
 791   /// <param name="isArrayElement">Whether the <paramref name="value"/> is inside an SQL array.</param>
 792   public delegate void WriteToBackendSync<TValue, TSizeInfo>( PgSQLTypeDatabaseData dbData, BackendABIHelper helper, By
 793
 794   /// <summary>
 795   /// This class contains information contained within database of a single SQL type.
 796   /// </summary>
 797   public sealed class PgSQLTypeDatabaseData
 798   {
 799      /// <summary>
 800      /// Creates a new instance of <see cref="PgSQLTypeDatabaseData"/> with given parameters.
 801      /// </summary>
 802      /// <param name="typeName">The textual name of the SQL type.</param>
 803      /// <param name="typeID">The ID (<c>oid</c>) of the SQL type.</param>
 804      /// <param name="arrayDelimiter">The textual delimiter character when values of this SQL type are within array.</p
 805      /// <param name="elementTypeID">The ID (<c>oid</c>) of element type, if this SQL type is an array.</param>
 57806      public PgSQLTypeDatabaseData(
 57807         String typeName,
 57808         Int32 typeID,
 57809         String arrayDelimiter,
 57810         Int32 elementTypeID
 57811         )
 812      {
 57813         this.TypeName = typeName;
 57814         this.TypeID = typeID;
 57815         this.ArrayDelimiter = arrayDelimiter;
 57816         this.ElementTypeID = elementTypeID;
 57817      }
 818
 819      /// <summary>
 820      /// Gets the textual name of the SQL type.
 821      /// </summary>
 822      /// <value>The textual name of the SQL type.</value>
 57823      public String TypeName { get; }
 824
 825      /// <summary>
 826      /// Gets the ID (<c>oid</c>) of the SQL type.
 827      /// </summary>
 828      /// <value>The ID (<c>oid</c>) of the SQL type.</value>
 1165829      public Int32 TypeID { get; }
 830
 831      /// <summary>
 832      /// Gets the ID (<c>oid</c>) of element type, if this SQL type is an array.
 833      /// </summary>
 834      /// <value>The ID (<c>oid</c>) of element type, if this SQL type is an array.</value>
 534835      public Int32 ElementTypeID { get; }
 836
 837      /// <summary>
 838      /// Gets the textual delimiter character when values of this SQL type are within array.
 839      /// </summary>
 840      /// <value>The textual delimiter character when values of this SQL type are within array.</value>
 841      /// <remarks>
 842      /// This is <see cref="String"/> instead of <see cref="Char"/> in case we get exotic stuff like surrogate pairs he
 843      /// </remarks>
 334844      public String ArrayDelimiter { get; }
 845   }
 846
 847   /// <summary>
 848   /// This class contains PostgreSQL-related extensions for types defined outside this assembly.
 849   /// </summary>
 850   public static partial class CBAMExtensions
 851   {
 852
 853      /// <summary>
 854      /// Writes <see cref="Int32"/> value to this byte array, in endianness expected by PostgreSQL backend (big-endian)
 855      /// </summary>
 856      /// <param name="array">This <see cref="Byte"/> array.</param>
 857      /// <param name="index">The index in <paramref name="array"/> where to write the <paramref name="value"/>.</param>
 858      /// <param name="value">The <see cref="Int32"/> value to write.</param>
 859      /// <returns>The <paramref name="array"/>.</returns>
 860      /// <exception cref="NullReferenceException">If this <see cref="Byte"/> array is <c>null</c>.</exception>
 861      /// <exception cref="IndexOutOfRangeException">If <paramref name="index"/> is out of valid range.</exception>
 862      public static Byte[] WritePgInt32( this Byte[] array, ref Int32 index, Int32 value )
 863      {
 864         array.WriteInt32BEToBytes( ref index, value );
 865         return array;
 866      }
 867
 868      /// <summary>
 869      /// Reads <see cref="Int32"/> value from this byte array, in endianness specified by PostgreSQL backend (big-endie
 870      /// </summary>
 871      /// <param name="array">This <see cref="Byte"/> array.</param>
 872      /// <param name="index">The index in <paramref name="array"/> where to start reading for <see cref="Int32"/>value.
 873      /// <returns>Deserialized <see cref="Int32"/>.</returns>
 874      /// <exception cref="NullReferenceException">If this <see cref="Byte"/> array is <c>null</c>.</exception>
 875      /// <exception cref="IndexOutOfRangeException">If <paramref name="index"/> is out of valid range.</exception>
 876      public static Int32 ReadPgInt32( this Byte[] array, ref Int32 index )
 877      {
 878         return array.ReadInt32BEFromBytes( ref index );
 879      }
 880
 881   }
 882}
 883
 884public static partial class E_CBAM
 885{
 886   private const Int32 NULL_BYTE_COUNT = -1;
 887
 888   /// <summary>
 889   /// This is helper method to first read <see cref="Int32"/> as size of data incoming, and if it is greater or equal t
 890   /// </summary>
 891   /// <param name="typeFunctionality">This <see cref="PgSQLTypeFunctionality"/>.</param>
 892   /// <param name="dataFormat">The <see cref="DataFormat"/> the value is being sent by backend.</param>
 893   /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 894   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 895   /// <param name="stream">The <see cref="StreamReaderWithResizableBufferAndLimitedSize"/> to use to read binary data f
 896   /// <returns>Asynchronously returns the CLR object deserialized from <paramref name="stream"/>, or <c>null</c>.</retu
 897   public static async ValueTask<(Object Value, Int32 BytesReadFromStream)> ReadBackendValueCheckNull(
 898      this PgSQLTypeFunctionality typeFunctionality,
 899      DataFormat dataFormat,
 900      PgSQLTypeDatabaseData boundData,
 901      BackendABIHelper helper,
 902      StreamReaderWithResizableBufferAndLimitedSize stream
 903      )
 904   {
 905      await stream.ReadOrThrow( sizeof( Int32 ) );
 906      var byteCount = 0;
 907      var length = stream.Buffer.ReadPgInt32( ref byteCount );
 908      Object retVal;
 909      if ( length >= 0 )
 910      {
 911         byteCount += length;
 912         using ( var limitedStream = stream.CreateWithLimitedSizeAndSharedBuffer( length ) )
 913         {
 914            try
 915            {
 916               retVal = await typeFunctionality.ReadBackendValueAsync(
 917                  dataFormat,
 918                  boundData,
 919                  helper,
 920                  limitedStream
 921                  );
 922            }
 923            finally
 924            {
 925               try
 926               {
 927                  // TODO this might not be necessary now with DisposeAsync delegate always called by AsyncEnumerator...
 928                  await limitedStream.SkipThroughRemainingBytes();
 929               }
 930               catch
 931               {
 932                  // Ignore
 933               }
 934            }
 935         }
 936      }
 937      else
 938      {
 939         retVal = null;
 940      }
 941
 942      return (retVal, byteCount);
 943   }
 944
 945   /// <summary>
 946   /// This is helper method to first check whether given value is <c>null</c>, and then return <c>-1</c>, or invoke <se
 947   /// </summary>
 948   /// <param name="typeFunctionality">This <see cref="PgSQLTypeFunctionality"/>.</param>
 949   /// <param name="dataFormat">The <see cref="DataFormat"/> value is being sent to backend.</param>
 950   /// <param name="dbData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific to 
 951   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 952   /// <param name="value">The value recognized by this <see cref="PgSQLTypeFunctionality"/>.</param>
 953   /// <param name="isArrayElement">Whether the <paramref name="value"/> is being sent inside SQL array.</param>
 954   /// <returns>The <see cref="BackendSizeInfo"/> object containing the byte count and optional custom information. Will
 955   public static BackendSizeInfo GetBackendSizeCheckNull( this PgSQLTypeFunctionality typeFunctionality, DataFormat data
 956   {
 957      return value == null ? new BackendSizeInfo( NULL_BYTE_COUNT ) : typeFunctionality.GetBackendSize( dataFormat, dbDa
 958   }
 959
 960   /// <summary>
 961   /// This is helper method to first write the <see cref="BackendSizeInfo.ByteCount"/> property of <see cref="BackendSi
 962   /// </summary>
 963   /// <param name="typeFunctionality">This <see cref="PgSQLTypeFunctionality"/>.</param>
 964   /// <param name="dataFormat">The <see cref="DataFormat"/> of the data, as expected by backend.</param>
 965   /// <param name="boundData">The <see cref="PgSQLTypeDatabaseData"/> containing information about this type, specific 
 966   /// <param name="helper">The <see cref="BackendABIHelper"/> application binary interface helper.</param>
 967   /// <param name="stream">The <see cref="StreamWriterWithResizableBufferAndLimitedSize"/> to write binary data to.</pa
 968   /// <param name="value">The CLR object to serialize.</param>
 969   /// <param name="additionalInfoFromSize">The the <see cref="BackendSizeInfo"/>, as returned by <see cref="PgSQLTypeFu
 970   /// <param name="isArrayElement">Whether <paramref name="value"/> is being sent inside SQL array.</param>
 971   /// <returns>Asychronously returns after the <paramref name="value"/> has been serialized.</returns>
 972   public static async Task WriteBackendValueCheckNull(
 973      this PgSQLTypeFunctionality typeFunctionality,
 974      DataFormat dataFormat,
 975      PgSQLTypeDatabaseData boundData,
 976      BackendABIHelper helper,
 977      StreamWriterWithResizableBufferAndLimitedSize stream,
 978      Object value,
 979      BackendSizeInfo additionalInfoFromSize,
 980      Boolean isArrayElement
 981      )
 982   {
 983      (var offset, var count) = stream.ReserveBufferSegment( sizeof( Int32 ) );
 984      stream.Buffer.WritePgInt32( ref offset, value == null ? NULL_BYTE_COUNT : additionalInfoFromSize.ByteCount );
 985      if ( additionalInfoFromSize.ByteCount > 0 )
 986      {
 987         await typeFunctionality.WriteBackendValueAsync( dataFormat, boundData, helper, stream, value, additionalInfoFro
 988      }
 989      await stream.FlushAsync();
 990   }
 991}
 992